diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index feb47f82f40..b744356115e 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -33,6 +33,10 @@ jobs: needs: - build timeout-minutes: 45 + strategy: + matrix: + suite: [Oid4VpTestSuite, Oid4VciHaipTestSuite, Oid4VciNonHaipTestSuite] + fail-fast: false steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -40,8 +44,5 @@ jobs: name: Integration test setup uses: ./.github/actions/integration-test-setup - - name: Build tar keycloak-quarkus-dist - run: ./mvnw package -pl quarkus/server/,quarkus/dist/ - - name: Run conformance tests - run: ./mvnw package -f tests/conformance/pom.xml + run: ./mvnw package -f tests/conformance/pom.xml -Dtest=${{ matrix.suite }} diff --git a/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/ConformanceTestFrameworkExtension.java b/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/ConformanceTestFrameworkExtension.java index e8543594343..cd00b3df318 100644 --- a/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/ConformanceTestFrameworkExtension.java +++ b/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/ConformanceTestFrameworkExtension.java @@ -26,6 +26,6 @@ public class ConformanceTestFrameworkExtension implements TestFrameworkExtension @Override public List> suppliers() { - return List.of(new OpenIdConformanceSuiteSupplier()); + return List.of(new OpenIdConformanceServerSupplier()); } } diff --git a/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/OpenIdConformanceSuite.java b/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/OpenIdConformanceServer.java similarity index 91% rename from test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/OpenIdConformanceSuite.java rename to test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/OpenIdConformanceServer.java index 3fc7428dd75..75d1554f17b 100644 --- a/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/OpenIdConformanceSuite.java +++ b/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/OpenIdConformanceServer.java @@ -37,9 +37,9 @@ import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.utility.DockerImageName; -public final class OpenIdConformanceSuite implements AutoCloseable { +public final class OpenIdConformanceServer implements AutoCloseable { - private static final Logger LOGGER = Logger.getLogger(OpenIdConformanceSuite.class); + private static final Logger LOGGER = Logger.getLogger(OpenIdConformanceServer.class); public static final String MONGODB_CONTAINER = "mongodb"; public static final String CONFORMANCE_CONTAINER = "conformance"; @@ -53,7 +53,7 @@ public final class OpenIdConformanceSuite implements AutoCloseable { private static final String NGINX_CERTIFICATE_PATH = "/etc/ssl/certs/nginx-selfsigned.crt"; - private static OpenIdConformanceSuite instance; + private static OpenIdConformanceServer instance; private final Network network; private final GenericContainer mongo; @@ -63,8 +63,8 @@ public final class OpenIdConformanceSuite implements AutoCloseable { private final URI baseUri; private final X509Certificate nginxCertificate; - private OpenIdConformanceSuite(Network network, GenericContainer mongo, GenericContainer server, - GenericContainer nginx, URI baseUri, SSLContext sslContext, X509Certificate nginxCertificate) { + private OpenIdConformanceServer(Network network, GenericContainer mongo, GenericContainer server, + GenericContainer nginx, URI baseUri, SSLContext sslContext, X509Certificate nginxCertificate) { this.network = network; this.mongo = mongo; this.server = server; @@ -78,7 +78,7 @@ public final class OpenIdConformanceSuite implements AutoCloseable { * The suite is a singleton as it is also used to discover module variants while tests are collected, before * the test framework injects it. */ - public static synchronized OpenIdConformanceSuite instance() { + public static synchronized OpenIdConformanceServer instance() { if (instance == null) { // Must be exposed before the containers start so they can reach Keycloak on the Docker host Testcontainers.exposeHostPorts(KEYCLOAK_BASE_URI.getPort()); @@ -87,7 +87,7 @@ public final class OpenIdConformanceSuite implements AutoCloseable { return instance; } - private static OpenIdConformanceSuite start() { + private static OpenIdConformanceServer start() { LOGGER.trace("Starting Conformance Suite with MongoDB (storage) and Nginx (access point)"); Network network = Network.newNetwork(); @@ -131,8 +131,8 @@ public final class OpenIdConformanceSuite implements AutoCloseable { URI baseUri = URI.create("https://" + nginx.getHost() + ":" + nginx.getMappedPort(8443)); X509Certificate nginxCertificate = nginxCertificate(nginx); SSLContext sslContext = sslContextTrusting(nginxCertificate); - OpenIdConformanceSuite suite = - new OpenIdConformanceSuite(network, mongo, server, nginx, baseUri, sslContext, nginxCertificate); + OpenIdConformanceServer suite = + new OpenIdConformanceServer(network, mongo, server, nginx, baseUri, sslContext, nginxCertificate); suite.client().waitUntilAvailable(Duration.ofMinutes(4)); return suite; } catch (RuntimeException e) { @@ -164,7 +164,7 @@ public final class OpenIdConformanceSuite implements AutoCloseable { public void close() { List.of(nginx, server, mongo).forEach(GenericContainer::stop); network.close(); - synchronized (OpenIdConformanceSuite.class) { + synchronized (OpenIdConformanceServer.class) { if (instance == this) { instance = null; } diff --git a/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/OpenIdConformanceSuiteSupplier.java b/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/OpenIdConformanceServerSupplier.java similarity index 69% rename from test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/OpenIdConformanceSuiteSupplier.java rename to test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/OpenIdConformanceServerSupplier.java index e94f53d9c75..a470fcae0a7 100644 --- a/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/OpenIdConformanceSuiteSupplier.java +++ b/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/OpenIdConformanceServerSupplier.java @@ -17,18 +17,18 @@ package org.keycloak.testframework.conformance; -import org.keycloak.testframework.conformance.annotations.InjectConformanceSuite; +import org.keycloak.testframework.conformance.annotations.InjectOpenIdConformanceServer; import org.keycloak.testframework.injection.InstanceContext; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.injection.RequestedInstance; import org.keycloak.testframework.injection.Supplier; -public class OpenIdConformanceSuiteSupplier implements Supplier { +public class OpenIdConformanceServerSupplier implements Supplier { @Override - public OpenIdConformanceSuite getValue(InstanceContext instanceContext) { - return OpenIdConformanceSuite.instance(); + public OpenIdConformanceServer getValue(InstanceContext instanceContext) { + return OpenIdConformanceServer.instance(); } @Override @@ -37,13 +37,13 @@ public class OpenIdConformanceSuiteSupplier implements Supplier a, - RequestedInstance b) { + public boolean compatible(InstanceContext a, + RequestedInstance b) { return true; } @Override - public void close(InstanceContext instanceContext) { + public void close(InstanceContext instanceContext) { instanceContext.getValue().close(); } } diff --git a/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/annotations/InjectConformanceSuite.java b/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/annotations/InjectOpenIdConformanceServer.java similarity index 95% rename from test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/annotations/InjectConformanceSuite.java rename to test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/annotations/InjectOpenIdConformanceServer.java index 1561a6732b0..15b5b4ea690 100644 --- a/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/annotations/InjectConformanceSuite.java +++ b/test-framework/conformance-runner/src/main/java/org/keycloak/testframework/conformance/annotations/InjectOpenIdConformanceServer.java @@ -24,5 +24,5 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) -public @interface InjectConformanceSuite { +public @interface InjectOpenIdConformanceServer { } diff --git a/tests/conformance/README.md b/tests/conformance/README.md new file mode 100644 index 00000000000..abe6b88f163 --- /dev/null +++ b/tests/conformance/README.md @@ -0,0 +1,67 @@ +# Keycloak OpenID for Verifiable Credentials conformance tests + +This module runs Keycloak against the official [OpenID Foundation conformance suite](https://gitlab.com/openid/conformance-suite) +to verify that Keycloak's OpenID for Verifiable Credentials implementation conforms to the specifications. Each test +drives a real conformance suite test module (plan creation, module execution and result assertion) against a live +Keycloak server, so a passing run is evidence of spec conformance, not just internal correctness. + +The conformance suite itself (its `mongodb`, `conformance` and `nginx` containers) is started automatically via +Testcontainers; the suite version is pinned by the `conformance.version` property in the root `pom.xml` +(currently `release-v5.2.2`). Keycloak is started by the test framework as a distribution server over HTTPS and +reached by the suite at `https://host.testcontainers.internal:8443`. + +## What is covered + +The tests are grouped into three JUnit suites, one per profile/role, each with its own Keycloak server +configuration. CI runs every suite as a separate matrix job (`.github/workflows/conformance.yml`), so each +suite gets its own JVM. Running a single suite locally (see [Running the tests](#running-the-tests)) is equivalent. +Note that the combined `./mvnw package` invocation does **not** give per-suite process isolation: Surefire uses a +single reusable fork, so all three suites run in the same JVM. + +| Suite | Package | Role / profile | Client authentication | +|-------|---------|----------------|-----------------------| +| `Oid4VciHaipTestSuite` | `vci.haip` | OID4VCI issuer, **HAIP** (`oid4vci-1_0-issuer-haip-test-plan`) | client attestation + DPoP; includes the FAPI2 and mDoc modules | +| `Oid4VciNonHaipTestSuite` | `vci.nonhaip` | OID4VCI issuer, **non-HAIP** (`oid4vci-1_0-issuer-test-plan`, `fapi_profile=vci`) | `private_key_jwt` + DPoP | +| `Oid4VpTestSuite` | `vp` | OID4VP verifier (`oid4vp-1final-verifier-test-plan`) | wallet mock; plain and encrypted (`direct_post.jwt`) response modes | + +The suite/profile split is intentional: HAIP requires attestation-based client authentication, whereas the +conformance suite's issuer modules do not permit public or `client_secret` authentication, so the non-HAIP profile +uses `private_key_jwt`. Shared realm building blocks live in `vci/VciConformanceRealmUtil`; each profile's realm and +server configuration lives in `vci/haip` and `vci/nonhaip` respectively. + +## Prerequisites + +- JDK and Maven (use the `./mvnw` wrapper). +- A running container runtime — **Docker or Podman** — with permission to pull from `registry.gitlab.com`. +- Keycloak built locally so the Quarkus distribution artifact is in your local Maven repository: + + ```bash + ./mvnw install -DskipTests + ``` + +## Running the tests + +Run one suite (mirrors how CI runs them, one matrix job each): + +```bash +./mvnw package -f tests/conformance/pom.xml -Dtest=Oid4VciNonHaipTestSuite +./mvnw package -f tests/conformance/pom.xml -Dtest=Oid4VciHaipTestSuite +./mvnw package -f tests/conformance/pom.xml -Dtest=Oid4VpTestSuite +``` + +Run all suites together in one invocation (all three suites share a single Surefire reusable fork, i.e. the same +JVM — this does not reproduce the per-process isolation of the CI matrix jobs): + +```bash +./mvnw package -f tests/conformance/pom.xml +``` + +Run a single test class (fully qualified name; useful when iterating): + +```bash +./mvnw test -f tests/conformance/pom.xml \ + -Dtest='org.keycloak.tests.conformance.vci.nonhaip.issuer.IssuerHappyFlowTest' +``` + +The first run pulls the conformance suite container images, so allow extra time. Full logs for a failed conformance +module are printed to the test output to aid debugging. diff --git a/tests/conformance/pom.xml b/tests/conformance/pom.xml index 53301ecbe56..8ac5837339f 100644 --- a/tests/conformance/pom.xml +++ b/tests/conformance/pom.xml @@ -52,5 +52,10 @@ org.keycloak.testframework keycloak-test-framework-ui + + org.junit.platform + junit-platform-suite + test + diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/AbstractConformanceTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/AbstractConformanceTest.java index 20a250adedf..37b2c63e5cb 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/AbstractConformanceTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/AbstractConformanceTest.java @@ -22,8 +22,8 @@ import java.util.Map; import java.util.function.Consumer; import java.util.stream.Stream; -import org.keycloak.testframework.conformance.OpenIdConformanceSuite; -import org.keycloak.testframework.conformance.annotations.InjectConformanceSuite; +import org.keycloak.testframework.conformance.OpenIdConformanceServer; +import org.keycloak.testframework.conformance.annotations.InjectOpenIdConformanceServer; import org.keycloak.testframework.conformance.runner.BrowserInteraction; import org.keycloak.testframework.conformance.runner.ConformanceModuleResult; import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; @@ -53,8 +53,8 @@ public abstract class AbstractConformanceTest { @InjectCertificates(config = TlsCertificates.class) protected ManagedCertificates certificates; - @InjectConformanceSuite - protected OpenIdConformanceSuite suite; + @InjectOpenIdConformanceServer + protected OpenIdConformanceServer server; protected abstract Stream moduleVariants(); @@ -72,7 +72,7 @@ public abstract class AbstractConformanceTest { String name, ConformanceResult expectedResult, BrowserInteraction browserInteraction) { ConformanceModuleVariant template = new ConformanceModuleVariant(plan, planVariant, name, Map.of(), expectedResult, browserInteraction); - return OpenIdConformanceSuite.instance().client() + return OpenIdConformanceServer.instance().client() .discoverModuleVariants(plan, planVariant, name, suiteConfig(template)) .map(moduleVariant -> new ConformanceModuleVariant(plan, planVariant, name, moduleVariant, expectedResult, browserInteraction)); @@ -86,7 +86,7 @@ public abstract class AbstractConformanceTest { List names, ConformanceResult expectedResult, BrowserInteraction browserInteraction) { ConformanceModuleVariant template = new ConformanceModuleVariant(plan, planVariant, names.get(0), Map.of(), expectedResult, browserInteraction); - Map>> discovered = OpenIdConformanceSuite.instance().client() + Map>> discovered = OpenIdConformanceServer.instance().client() .discoverModuleVariants(plan, planVariant, names, suiteConfig(template)); return names.stream().flatMap(name -> discovered.get(name).stream() .map(moduleVariant -> new ConformanceModuleVariant(plan, planVariant, name, moduleVariant, @@ -104,7 +104,7 @@ public abstract class AbstractConformanceTest { @ParameterizedTest @MethodSource("moduleVariants") public void conformance(ConformanceModuleVariant moduleVariant) { - ConformanceModuleResult result = suite.client() + ConformanceModuleResult result = server.client() .run(moduleVariant, suiteConfig(moduleVariant), interaction(moduleVariant)); if (!result.finishedWith(moduleVariant.expectedResult())) { LOGGER.errorf("Full logs of failed conformance module %s:%n%s", result.module(), result.logs().toPrettyString()); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/suites/Oid4VciHaipTestSuite.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/suites/Oid4VciHaipTestSuite.java new file mode 100644 index 00000000000..bb8f7e6515b --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/suites/Oid4VciHaipTestSuite.java @@ -0,0 +1,9 @@ +package org.keycloak.tests.conformance.suites; + +import org.junit.platform.suite.api.SelectPackages; +import org.junit.platform.suite.api.Suite; + +@Suite +@SelectPackages({"org.keycloak.tests.conformance.vci.haip"}) +public class Oid4VciHaipTestSuite { +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/suites/Oid4VciNonHaipTestSuite.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/suites/Oid4VciNonHaipTestSuite.java new file mode 100644 index 00000000000..210218cf92a --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/suites/Oid4VciNonHaipTestSuite.java @@ -0,0 +1,10 @@ +package org.keycloak.tests.conformance.suites; + +import org.junit.platform.suite.api.SelectPackages; +import org.junit.platform.suite.api.Suite; + + +@Suite +@SelectPackages({"org.keycloak.tests.conformance.vci.nonhaip"}) +public class Oid4VciNonHaipTestSuite { +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/suites/Oid4VpTestSuite.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/suites/Oid4VpTestSuite.java new file mode 100644 index 00000000000..6a9032c2732 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/suites/Oid4VpTestSuite.java @@ -0,0 +1,11 @@ +package org.keycloak.tests.conformance.suites; + +import org.junit.platform.suite.api.SelectPackages; +import org.junit.platform.suite.api.Suite; + +@Suite +@SelectPackages({ + "org.keycloak.tests.conformance.vp" +}) +public class Oid4VpTestSuite { +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/AbstractVciConformanceTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/AbstractVciConformanceTest.java index 92da17b51cb..d324166589a 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/AbstractVciConformanceTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/AbstractVciConformanceTest.java @@ -22,41 +22,92 @@ import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.function.Consumer; -import org.keycloak.testframework.conformance.OpenIdConformanceSuite; +import org.keycloak.testframework.conformance.OpenIdConformanceServer; import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; import org.keycloak.testframework.conformance.runner.ModuleRun; import org.keycloak.tests.conformance.AbstractConformanceTest; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; import com.fasterxml.jackson.databind.JsonNode; /** - * Baseline for OID4VCI conformance tests. Test classes inject a realm with {@link VciConformanceRealmConfig} or - * a subclass of it for additional configuration. + * Baseline for OID4VCI conformance tests, shared by both the HAIP and non-HAIP hierarchies. HAIP test classes inject + * a realm with {@link HaipVciConformanceRealmConfig} (or a subclass); non-HAIP tests extend + * {@link org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest} and inject + * {@link org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig} (or a subclass). Subclasses + * override the JWKS and plan-variant hooks below to supply the profile-specific configuration. */ public abstract class AbstractVciConformanceTest extends AbstractConformanceTest { - protected static final String HAIP_PLAN = "oid4vci-1_0-issuer-haip-test-plan"; + protected Map walletInitiated() { + return planVariant("wallet_initiated"); + } - // The plan variant pins every dimension but vci_credential_encryption, leaving the plain and encrypted - // variants to be discovered per module. mdoc is intentionally excluded as Keycloak does not support it. - protected static final Map WALLET_INITIATED = Map.of( - "credential_format", "sd_jwt_vc", - "vci_authorization_code_flow_variant", "wallet_initiated"); + protected Map issuerInitiated() { + return planVariant("issuer_initiated"); + } - protected static final Map ISSUER_INITIATED = Map.of( - "credential_format", "sd_jwt_vc", - "vci_authorization_code_flow_variant", "issuer_initiated"); + /** + * The plan variant selection sent to the conformance suite for the given authorization code flow variant. The + * HAIP plan pins every other dimension itself, so the baseline supplies only the credential format and flow + * variant; {@link org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest} overrides this + * to add the FAPI2 dimensions the generic non-HAIP plan does not pin. Credential encryption is intentionally + * omitted so it stays discovered per module (it defaults to plain). + */ + protected Map planVariant(String flowVariant) { + return Map.of( + "credential_format", "sd_jwt_vc", + "vci_authorization_code_flow_variant", flowVariant); + } @Override protected JsonNode suiteConfig(ConformanceModuleVariant module) { return VciSuiteConfig.create( - OpenIdConformanceSuite.KEYCLOAK_BASE_URI, - VciConformanceRealmConfig.credentialConfigurationId(module.planVariant().get("credential_format")), - VciConformanceRealmConfig.attesterJwks(), + OpenIdConformanceServer.KEYCLOAK_BASE_URI, + VciConformanceRealmUtil.credentialConfigurationId(module.planVariant().get("credential_format")), + clientAttesterJwks(), + keyAttestationJwks(), + clientJwks(), + client2Jwks(), VciTestSigningKey.caCertificatePem(), module.browserInteraction()).toJson(); } + /** + * The client attester JWKS advertised to the conformance suite for client attestation based authentication. + * HAIP publishes it; non-HAIP uses private_key_jwt and overrides this to {@code null}, which omits the client + * attestation material from the suite config. + */ + protected JsonNode clientAttesterJwks() { + return VciConformanceRealmUtil.attesterJwks(); + } + + /** + * The key attestation JWKS advertised to the conformance suite for key attestations on the credential proof. + * HAIP publishes it; non-HAIP overrides this to {@code null} by default and re-enables it only in the tests + * that exercise key attestation. + */ + protected JsonNode keyAttestationJwks() { + return VciConformanceRealmUtil.attesterJwks(); + } + + /** + * The private JWKS the conformance suite signs the first client's private_key_jwt assertions with. HAIP + * authenticates with client attestation and returns {@code null} here (the {@code jwks} client field is then + * omitted); non-HAIP overrides this to publish the conformance client's private key. + */ + protected JsonNode clientJwks() { + return null; + } + + /** + * The private JWKS for the second conformance client, which must differ from {@link #clientJwks()} for the + * multiple-clients module. HAIP returns {@code null}; non-HAIP overrides it. + */ + protected JsonNode client2Jwks() { + return null; + } + @Override protected Consumer interaction(ConformanceModuleVariant module) { if (!"issuer_initiated".equals(module.planVariant().get("vci_authorization_code_flow_variant"))) { @@ -66,7 +117,7 @@ public abstract class AbstractVciConformanceTest extends AbstractConformanceTest // endpoint. Keycloak creates the offer through the AIA login, and the suite then fetches it from the // delivered credential_offer_uri and runs the normal authorization code flow. String alias = suiteConfig(module).path("alias").asText(); - return moduleRun -> suite.client().visitTestEndpoint(alias, "credential_offer", + return moduleRun -> server.client().visitTestEndpoint(alias, "credential_offer", "credential_offer_uri=" + URLEncoder.encode(createCredentialOfferUri(), StandardCharsets.UTF_8)); } diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciAiaCredentialOffer.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciAiaCredentialOffer.java index f977bd51995..0d40e385f95 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciAiaCredentialOffer.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciAiaCredentialOffer.java @@ -25,7 +25,7 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import org.keycloak.representations.idm.oid4vc.VerifiableCredentialOfferActionConfig; -import org.keycloak.testframework.conformance.OpenIdConformanceSuite; +import org.keycloak.testframework.conformance.OpenIdConformanceServer; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; @@ -36,6 +36,11 @@ import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import static org.keycloak.constants.OID4VCIConstants.VERIFIABLE_CREDENTIAL_OFFER_PROVIDER_ID; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.APP_CLIENT; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.CREDENTIAL_CONFIGURATION_ID; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.HOLDER; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.PASSWORD; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.REALM; /** * Creates a credential offer through the {@code verifiable_credential_offer} application initiated action, the @@ -53,7 +58,7 @@ final class VciAiaCredentialOffer { } static String createOfferUri() { - URI keycloakUri = OpenIdConformanceSuite.KEYCLOAK_BASE_URI; + URI keycloakUri = OpenIdConformanceServer.KEYCLOAK_BASE_URI; ChromeOptions options = new ChromeOptions(); options.addArguments( @@ -71,8 +76,8 @@ final class VciAiaCredentialOffer { WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); wait.until(ExpectedConditions.presenceOfElementLocated(By.id("username"))); - driver.findElement(By.id("username")).sendKeys(VciConformanceRealmConfig.HOLDER); - driver.findElement(By.id("password")).sendKeys(VciConformanceRealmConfig.PASSWORD); + driver.findElement(By.id("username")).sendKeys(HOLDER); + driver.findElement(By.id("password")).sendKeys(PASSWORD); driver.findElement(By.id("kc-login")).click(); WebElement offerLink = wait.until( @@ -90,13 +95,13 @@ final class VciAiaCredentialOffer { private static String authorizationUrl(URI keycloakUri) { VerifiableCredentialOfferActionConfig actionConfig = new VerifiableCredentialOfferActionConfig(); - actionConfig.setCredentialConfigurationId(VciConformanceRealmConfig.CREDENTIAL_CONFIGURATION_ID); + actionConfig.setCredentialConfigurationId(CREDENTIAL_CONFIGURATION_ID); actionConfig.setPreAuthorized(false); - String realmBase = keycloakUri + "/realms/" + VciConformanceRealmConfig.REALM; + String realmBase = keycloakUri + "/realms/" + REALM; try { return realmBase + "/protocol/openid-connect/auth" - + "?client_id=" + VciConformanceRealmConfig.APP_CLIENT + + "?client_id=" + APP_CLIENT + "&redirect_uri=" + encode(realmBase + "/account/") + "&response_type=code&scope=openid" + "&kc_action=" + encode(VERIFIABLE_CREDENTIAL_OFFER_PROVIDER_ID + ":" + actionConfig.asEncodedParameter()); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciAttesterKey.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciAttesterKey.java index 2c2fd570234..cb923f5c2b5 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciAttesterKey.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciAttesterKey.java @@ -22,6 +22,8 @@ import org.keycloak.tests.conformance.ConformanceSigningKey; import com.fasterxml.jackson.databind.JsonNode; import org.bouncycastle.asn1.x509.KeyPurposeId; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.REALM; + /** * The attester key, generated at runtime so no private key material is committed to the repository. It is signed * by a CA so it serves both client attestation (verified against the trusted public JWKS) and key attestation @@ -32,9 +34,9 @@ public final class VciAttesterKey { static final String KID = "ct_client_attester_key"; - // The attestation EKU must match ATTESTER_ATTESTATION_EKU in VciConformanceRealmConfig. + // The attestation EKU must match ATTESTER_ATTESTATION_EKU in HaipVciConformanceRealmConfig. private static final ConformanceSigningKey KEY = ConformanceSigningKey.generate( - VciConformanceRealmConfig.REALM, KID, "OID4VCI Conformance Attester", + REALM, KID, "OID4VCI Conformance Attester", KeyPurposeId.id_kp_emailProtection); private VciAttesterKey() { @@ -44,7 +46,7 @@ public final class VciAttesterKey { return KEY.privateJwks(); } - static JsonNode publicJwks() { + public static JsonNode publicJwks() { return KEY.publicJwks(); } diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciConformanceRealmConfig.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciConformanceRealmUtil.java similarity index 60% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciConformanceRealmConfig.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciConformanceRealmUtil.java index 33be2a870e1..faefa4fd3fe 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciConformanceRealmConfig.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciConformanceRealmUtil.java @@ -25,15 +25,10 @@ import java.util.UUID; import org.keycloak.OID4VCConstants; import org.keycloak.VCFormat; -import org.keycloak.authentication.authenticators.client.AttestationBasedClientAuthenticator; -import org.keycloak.broker.trust.DefaultTrustIdentityProviderConfig; -import org.keycloak.broker.trust.DefaultTrustIdentityProviderFactory; -import org.keycloak.common.Profile; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.constants.OID4VCIConstants; import org.keycloak.crypto.Algorithm; import org.keycloak.crypto.KeyUse; -import org.keycloak.json.RawJsonValue; import org.keycloak.keys.Attributes; import org.keycloak.keys.GeneratedEcdhKeyProviderFactory; import org.keycloak.keys.JavaKeystoreKeyProviderFactory; @@ -47,37 +42,32 @@ import org.keycloak.protocol.oid4vc.issuance.mappers.OID4VCMapper; import org.keycloak.protocol.oid4vc.issuance.mappers.OID4VCStaticClaimMapper; import org.keycloak.protocol.oid4vc.model.CredentialScopeRepresentation; import org.keycloak.protocol.oid4vc.model.DisplayObject; -import org.keycloak.protocol.oidc.OIDCConfigAttributes; -import org.keycloak.representations.idm.ClientPolicyConditionRepresentation; -import org.keycloak.representations.idm.ClientPolicyExecutorRepresentation; -import org.keycloak.representations.idm.ClientPolicyRepresentation; -import org.keycloak.representations.idm.ClientProfileRepresentation; import org.keycloak.representations.idm.ComponentExportRepresentation; -import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; -import org.keycloak.services.clientpolicy.executor.PKCEEnforcerExecutorFactory; -import org.keycloak.services.clientpolicy.executor.RejectImplicitGrantExecutorFactory; -import org.keycloak.services.clientpolicy.executor.SecureParContentsExecutorFactory; -import org.keycloak.testframework.conformance.OpenIdConformanceSuite; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testframework.conformance.OpenIdConformanceServer; import org.keycloak.testframework.realm.ClientBuilder; import org.keycloak.testframework.realm.RealmBuilder; -import org.keycloak.testframework.realm.RealmConfig; import org.keycloak.testframework.realm.UserBuilder; -import org.keycloak.testframework.server.KeycloakServerConfig; -import org.keycloak.testframework.server.KeycloakServerConfigBuilder; -import org.keycloak.tests.conformance.ConformanceSigningKey; import org.keycloak.util.JsonSerialization; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; import static org.keycloak.OID4VCConstants.CRYPTOGRAPHIC_BINDING_METHOD_COSE_KEY; import static org.keycloak.OID4VCConstants.OID4VCI_ENABLED_ATTRIBUTE_KEY; import static org.keycloak.models.Constants.CREATE_DEFAULT_CLIENT_SCOPES; import static org.keycloak.models.Constants.DEFAULT_ROLES_ROLE_PREFIX; -public class VciConformanceRealmConfig implements RealmConfig { +/** + * Shared building blocks for the OID4VCI conformance realm. The pieces here are identical for HAIP and non-HAIP: + * realm defaults, the SD-JWT and mDoc credential scopes with their protocol mappers, the holder user, the account + * app client, the signing and encryption key providers, and the common (auth-agnostic) conformance client shape. + * + * The HAIP and non-HAIP realm configs compose these helpers and add only what differs (client authentication, + * client policies, and trust anchors). Kept as a utility rather than a base class so each config reads as a flat + * recipe and the two variants never share behaviour implicitly. + */ +public final class VciConformanceRealmUtil { public static final String REALM = "oid4vci"; public static final String HOLDER = "alice"; @@ -93,11 +83,22 @@ public class VciConformanceRealmConfig implements RealmConfig { public static final String MDOC_NAMESPACE = "org.iso.18013.5.1"; // The credential_format plan variant value the conformance suite uses for ISO mdoc, see VCI1FinalCredentialFormat public static final String MDOC_CREDENTIAL_FORMAT_VARIANT = "mdoc"; - public static final String CONFORMANCE_CALLBACK = OpenIdConformanceSuite.INTERNAL_BASE_URI + "/test/a/keycloak/callback"; - public static final String TRUST_IDP_ALIAS = "conformance-client-attester"; - public static final String X509_TRUST_IDP_ALIAS = "conformance-attester-x509"; - // The attester leaf certificate is generated with the emailProtection extended key usage. - private static final String ATTESTER_ATTESTATION_EKU = "1.3.6.1.5.5.7.3.4"; + public static final String CONFORMANCE_CALLBACK = OpenIdConformanceServer.INTERNAL_BASE_URI + "/test/a/keycloak/callback"; + + // FAPI2 requires the TLS layer to only offer BCP195 (RFC 9325) recommended cipher suites for TLS 1.2. The + // default JVM cipher list includes non-recommended suites, which the conformance suite TLS checks reject, so + // the test server is restricted to the recommended AEAD suites. The TLS 1.3 suites must be listed too, as + // restricting the cipher list would otherwise disable TLS 1.3 entirely and the suite checks that TLS 1.3 is + // negotiable where offered. Both the HAIP and non-HAIP servers run the FAPI2-family TLS checks, so they share + // this list. + public static final String TLS_PROTOCOLS = "TLSv1.3,TLSv1.2"; + public static final String BCP195_CIPHERS = String.join(",", + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"); /** * Maps the conformance suite {@code credential_format} plan variant to the matching credential configuration id @@ -109,9 +110,17 @@ public class VciConformanceRealmConfig implements RealmConfig { : CREDENTIAL_CONFIGURATION_ID; } - @Override - public RealmBuilder configure(RealmBuilder realm) { - realm.name(REALM) + public static JsonNode attesterJwks() { + return VciAttesterKey.privateJwks(); + } + + /** + * Applies the realm configuration shared by every OID4VCI conformance variant: realm-level attributes, the two + * credential scopes with their protocol mappers, and the holder user. Callers add the clients, client policies, + * trust anchors and key providers that differ per variant. + */ + public static RealmBuilder applyCommon(RealmBuilder realm) { + return realm.name(REALM) .eventsEnabled(true) .eventsListeners("jboss-logging") .verifiableCredentialsEnabled(true) @@ -138,77 +147,47 @@ public class VciConformanceRealmConfig implements RealmConfig { .realmRoles(DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM) .verifiableCredential(SD_JWT_SCOPE) .verifiableCredential(MDOC_SCOPE) - .build()) - .clients(conformanceClient(CLIENT, false), conformanceClient(CLIENT2, true), appClient()) - .clientProfile(haipClientProfile()) - .clientPolicy(haipClientPolicy()) - .update(rep -> { - MultivaluedHashMap components = rep.getComponents(); - if (components == null) { - components = new MultivaluedHashMap<>(); - rep.setComponents(components); - } - components.add(KeyProvider.class.getName(), conformanceSigningKeyProvider()); - components.add(KeyProvider.class.getName(), conformanceEcdhEncryptionKeyProvider()); - rep.setIdentityProviders(List.of(attesterTrustIdentityProvider(), attesterX509TrustIdentityProvider())); - }); - return realm; + .build()); } - public static JsonNode attesterJwks() { - return VciAttesterKey.privateJwks(); - } - - private IdentityProviderRepresentation attesterTrustIdentityProvider() { - IdentityProviderRepresentation trust = new IdentityProviderRepresentation(); - trust.setAlias(TRUST_IDP_ALIAS); - trust.setProviderId(DefaultTrustIdentityProviderFactory.PROVIDER_ID); - trust.setEnabled(true); - trust.setConfig(Map.of(DefaultTrustIdentityProviderConfig.TRUSTED_JWKS, VciAttesterKey.publicJwks().toString())); - return trust; - } - - // Trusts the key attestation x5c certificate chain against the attester CA. Key attestations carry an x5c - // header, so Keycloak must be able to validate the chain against a configured X.509 trust domain. - private IdentityProviderRepresentation attesterX509TrustIdentityProvider() { - IdentityProviderRepresentation trust = new IdentityProviderRepresentation(); - trust.setAlias(X509_TRUST_IDP_ALIAS); - trust.setProviderId(DefaultTrustIdentityProviderFactory.PROVIDER_ID); - trust.setEnabled(true); - trust.setConfig(Map.of( - DefaultTrustIdentityProviderConfig.USE_X509, "true", - DefaultTrustIdentityProviderConfig.TRUSTED_CERTIFICATES, VciAttesterKey.caCertificatePem(), - DefaultTrustIdentityProviderConfig.REQUIRED_EXTENDED_KEY_USAGES, ATTESTER_ATTESTATION_EKU)); - return trust; - } - - private ClientBuilder conformanceClient(String clientId, boolean wildcardRedirect) { + /** + * Common conformance client shape without any client authentication. HAIP and non-HAIP configs add their own + * authentication (attestation-based vs public + PKCE) on top of this. + */ + public static ClientBuilder baseConformanceClient(String clientId, boolean wildcardRedirect) { return ClientBuilder.create(clientId) .serviceAccountsEnabled(false) .directAccessGrantsEnabled(false) - .authenticatorType(AttestationBasedClientAuthenticator.PROVIDER_ID) - .attribute(AttestationBasedClientAuthenticator.OAUTH_CLIENT_ATTESTATION_CONFIG_TRUST_IDPS, TRUST_IDP_ALIAS) - .attribute(OID4VCIConstants.OID4VCI_ATTESTER_TRUST_IDPS_ATTR, TRUST_IDP_ALIAS + "," + X509_TRUST_IDP_ALIAS) - // HAIP requires DPoP sender constraining, so the token endpoint must reject a request without a - // DPoP proof (the holder-of-key conformance module checks exactly this) - .attribute(OIDCConfigAttributes.DPOP_BOUND_ACCESS_TOKENS, "true") .defaultClientScopes("basic", "profile", "roles") .optionalClientScopes(SD_JWT_SCOPE, MDOC_SCOPE, "email") .attribute(OID4VCI_ENABLED_ATTRIBUTE_KEY, "true") .redirectUris(CONFORMANCE_CALLBACK + (wildcardRedirect ? "*" : "")) - .webOrigins(OpenIdConformanceSuite.INTERNAL_BASE_URI.toString()); + .webOrigins(OpenIdConformanceServer.INTERNAL_BASE_URI.toString()); } - private ClientBuilder appClient() { + public static ClientBuilder appClient() { return ClientBuilder.create(APP_CLIENT) .publicClient(true) .serviceAccountsEnabled(false) .directAccessGrantsEnabled(false) - .redirectUris(OpenIdConformanceSuite.KEYCLOAK_BASE_URI + "/realms/" + REALM + "/account/*") + .redirectUris(OpenIdConformanceServer.KEYCLOAK_BASE_URI + "/realms/" + REALM + "/account/*") .defaultClientScopes("basic", "profile", "roles"); } - private CredentialScopeRepresentation createSdJwtCredentialScope() { + /** + * Adds the signing and ECDH encryption key providers used by every variant to the realm representation. + */ + public static void applyKeyProviders(RealmRepresentation rep) { + MultivaluedHashMap components = rep.getComponents(); + if (components == null) { + components = new MultivaluedHashMap<>(); + rep.setComponents(components); + } + components.add(KeyProvider.class.getName(), conformanceSigningKeyProvider()); + components.add(KeyProvider.class.getName(), conformanceEcdhEncryptionKeyProvider()); + } + + private static CredentialScopeRepresentation createSdJwtCredentialScope() { CredentialScopeRepresentation scope = new CredentialScopeRepresentation(SD_JWT_SCOPE) .setIncludeInTokenScope(true) .setExpiryInSeconds(300) @@ -232,7 +211,7 @@ public class VciConformanceRealmConfig implements RealmConfig { return scope; } - private CredentialScopeRepresentation createMdocCredentialScope() { + private static CredentialScopeRepresentation createMdocCredentialScope() { CredentialScopeRepresentation scope = new CredentialScopeRepresentation(MDOC_SCOPE) .setIncludeInTokenScope(true) .setExpiryInSeconds(300) @@ -254,7 +233,7 @@ public class VciConformanceRealmConfig implements RealmConfig { return scope; } - private ComponentExportRepresentation conformanceSigningKeyProvider() { + private static ComponentExportRepresentation conformanceSigningKeyProvider() { ComponentExportRepresentation keyProvider = new ComponentExportRepresentation(); keyProvider.setName("oid4vci-conformance-signing-key"); keyProvider.setId(UUID.randomUUID().toString()); @@ -274,7 +253,7 @@ public class VciConformanceRealmConfig implements RealmConfig { } // The conformance suite wallet requests ECDH-ES encrypted credential responses - private ComponentExportRepresentation conformanceEcdhEncryptionKeyProvider() { + private static ComponentExportRepresentation conformanceEcdhEncryptionKeyProvider() { ComponentExportRepresentation keyProvider = new ComponentExportRepresentation(); keyProvider.setName("oid4vci-conformance-ecdh-encryption-key"); keyProvider.setId(UUID.randomUUID().toString()); @@ -288,7 +267,7 @@ public class VciConformanceRealmConfig implements RealmConfig { return keyProvider; } - private List protocolMappers(String scopeName) { + private static List protocolMappers(String scopeName) { return List.of( mapper("did-mapper", "oid4vc-subject-id-mapper", Map.of("claim.name", OID4VCConstants.CLAIM_NAME_SUBJECT_ID, "userAttribute", "did")), mapper("email-mapper", "oid4vc-user-attribute-mapper", Map.of("claim.name", "email", "userAttribute", "email")), @@ -313,7 +292,7 @@ public class VciConformanceRealmConfig implements RealmConfig { // 18013-5 marks a fixed set of org.iso.18013.5.1 data elements mandatory for an mDL, so a conformant // issuer must emit all of them (the suite's EnsureMdocMdlMandatoryDataElementsPresent check enforces this). // The holder-specific ones come from user attributes; the rest are static document values. - private List mdocProtocolMappers() { + private static List mdocProtocolMappers() { return List.of( mdocMapper("did-mapper", "oid4vc-subject-id-mapper", "id", "did"), mdocMapper("given-name-mapper", "oid4vc-user-attribute-mapper", "given_name", "firstName"), @@ -329,21 +308,21 @@ public class VciConformanceRealmConfig implements RealmConfig { mdocStaticMapper("driving-privileges-mapper", "driving_privileges", "[]")); } - private ProtocolMapperRepresentation mdocStaticMapper(String name, String claimName, String value) { + private static ProtocolMapperRepresentation mdocStaticMapper(String name, String claimName, String value) { return mapper(name, OID4VCStaticClaimMapper.MAPPER_ID, Map.of( OID4VCMapper.CLAIM_NAME, claimName, OID4VCStaticClaimMapper.STATIC_CLAIM_KEY, value, OID4VCMapper.MDOC_NAMESPACE, MDOC_NAMESPACE)); } - private ProtocolMapperRepresentation mdocMapper(String name, String type, String claimName, String userAttribute) { + private static ProtocolMapperRepresentation mdocMapper(String name, String type, String claimName, String userAttribute) { return mapper(name, type, Map.of( OID4VCMapper.CLAIM_NAME, claimName, OID4VCMapper.USER_ATTRIBUTE_KEY, userAttribute, OID4VCMapper.MDOC_NAMESPACE, MDOC_NAMESPACE)); } - private ProtocolMapperRepresentation mapper(String name, String type, Map config) { + private static ProtocolMapperRepresentation mapper(String name, String type, Map config) { ProtocolMapperRepresentation mapper = new ProtocolMapperRepresentation(); mapper.setName(name); mapper.setId(UUID.randomUUID().toString()); @@ -352,80 +331,4 @@ public class VciConformanceRealmConfig implements RealmConfig { mapper.setConfig(config); return mapper; } - - public static final String HAIP_CLIENT_PROFILE = "oid4vc-haip-profile"; - - private ClientProfileRepresentation haipClientProfile() { - ClientProfileRepresentation profile = new ClientProfileRepresentation(); - profile.setName(HAIP_CLIENT_PROFILE); - profile.setDescription("Enforces the OpenID4VC High Assurance Interoperability Profile 1.0"); - profile.setExecutors(List.of( - executor(SecureParContentsExecutorFactory.PROVIDER_ID, JsonNodeFactory.instance.objectNode()), - executor(RejectImplicitGrantExecutorFactory.PROVIDER_ID, - JsonNodeFactory.instance.objectNode().put("auto-configure", false)), - executor(PKCEEnforcerExecutorFactory.PROVIDER_ID, - JsonNodeFactory.instance.objectNode().put("auto-configure", false)))); - return profile; - } - - private ClientPolicyRepresentation haipClientPolicy() { - ClientPolicyRepresentation policy = new ClientPolicyRepresentation(); - policy.setName("oid4vc-haip-policy"); - policy.setDescription("Enables the oid4vc-haip-profile for OID4VCI clients"); - policy.setEnabled(true); - - ClientPolicyConditionRepresentation condition = new ClientPolicyConditionRepresentation(); - condition.setConditionProviderId("client-attributes"); - ObjectNode config = JsonNodeFactory.instance.objectNode(); - config.put("attributes", JsonSerialization.valueAsString(List.of(Map.of( - "key", OID4VCI_ENABLED_ATTRIBUTE_KEY, - "value", String.valueOf(true))))); - condition.setConfiguration(RawJsonValue.of(config)); - - policy.setConditions(List.of(condition)); - policy.setProfiles(List.of(HAIP_CLIENT_PROFILE)); - return policy; - } - - private ClientPolicyExecutorRepresentation executor(String providerId, JsonNode config) { - ClientPolicyExecutorRepresentation executor = new ClientPolicyExecutorRepresentation(); - executor.setExecutorProviderId(providerId); - executor.setConfiguration(RawJsonValue.of(config)); - return executor; - } - - public static class ConsentRequiredRealmConfig extends VciConformanceRealmConfig { - - @Override - public RealmBuilder configure(RealmBuilder realm) { - return super.configure(realm).update(rep -> rep.getClients().stream() - .filter(client -> CLIENT.equals(client.getClientId()) || CLIENT2.equals(client.getClientId())) - .forEach(client -> client.setConsentRequired(true))); - } - } - - public static class ServerConfig implements KeycloakServerConfig { - - // FAPI2 requires the TLS layer to only offer BCP195 (RFC 9325) recommended cipher suites for TLS 1.2. - // The default JVM cipher list includes non recommended suites, which the conformance suite TLS checks - // reject, so the test server is restricted to the recommended AEAD suites. The TLS 1.3 suites must be - // listed too, as restricting the cipher list would otherwise disable TLS 1.3 entirely and the suite - // checks that TLS 1.3 is negotiable where offered. - private static final String BCP195_CIPHERS = String.join(",", - "TLS_AES_128_GCM_SHA256", - "TLS_AES_256_GCM_SHA384", - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"); - - @Override - public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) { - return config.features(Profile.Feature.OID4VC_VCI, Profile.Feature.OID4VC_MDOC, Profile.Feature.CLIENT_AUTH_ABCA) - .option("hostname", OpenIdConformanceSuite.KEYCLOAK_BASE_URI.toString()) - .spiOption("keys", "java-keystore", "keystores-path", ConformanceSigningKey.keystoresBaseDir()) - .option("https-protocols", "TLSv1.3,TLSv1.2") - .option("https-cipher-suites", BCP195_CIPHERS); - } - } } diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciSuiteConfig.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciSuiteConfig.java index a2e618609d0..85bcde860d4 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciSuiteConfig.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciSuiteConfig.java @@ -24,9 +24,16 @@ import org.keycloak.testframework.conformance.runner.BrowserFlow; import org.keycloak.testframework.conformance.runner.BrowserInteraction; import org.keycloak.util.JsonSerialization; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.CLIENT; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.CLIENT2; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.CONFORMANCE_CALLBACK; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.HOLDER; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.PASSWORD; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.REALM; /** * The test configuration uploaded to the conformance suite. The structure is defined by the suite and issuer @@ -40,29 +47,37 @@ record VciSuiteConfig( Client client2, List browser) { - static VciSuiteConfig create(URI keycloakBaseUri, String credentialConfigurationId, JsonNode attesterJwks, - String trustAnchorPem, BrowserInteraction browserInteraction) { + static VciSuiteConfig create(URI keycloakBaseUri, String credentialConfigurationId, JsonNode clientAttesterJwks, + JsonNode keyAttestationJwks, JsonNode clientJwks, JsonNode client2Jwks, String trustAnchorPem, + BrowserInteraction browserInteraction) { + // Client attestation (HAIP) and key attestation (HAIP and non-HAIP key-attestation tests) are independent: + // the client_attestation_* fields are only emitted when a client attester JWKS is supplied, so a non-HAIP + // private_key_jwt client that still exercises key attestation advertises only key_attestation_jwks. + String clientAttestationIssuer = clientAttesterJwks == null ? null : "https://example.com/client-attester"; return new VciSuiteConfig( "keycloak", - new Vci(keycloakBaseUri + "/realms/" + VciConformanceRealmConfig.REALM, + new Vci(keycloakBaseUri + "/realms/" + REALM, credentialConfigurationId, - "https://example.com/client-attester", - attesterJwks, - attesterJwks), + clientAttestationIssuer, + clientAttesterJwks, + keyAttestationJwks), new Credential(trustAnchorPem, trustAnchorPem), - new Client(VciConformanceRealmConfig.CLIENT), - new Client(VciConformanceRealmConfig.CLIENT2), + // HAIP passes null client JWKS (it authenticates with client attestation); non-HAIP passes the + // distinct private JWKS the suite signs each client's private_key_jwt assertions with. + new Client(CLIENT, clientJwks), + new Client(CLIENT2, client2Jwks), browserInteraction.browserFlows(new BrowserInteraction.BrowserContext( - VciConformanceRealmConfig.REALM, - VciConformanceRealmConfig.HOLDER, - VciConformanceRealmConfig.PASSWORD, - VciConformanceRealmConfig.CONFORMANCE_CALLBACK))); + REALM, + HOLDER, + PASSWORD, + CONFORMANCE_CALLBACK))); } JsonNode toJson() { return JsonSerialization.writeValueAsNode(this); } + @JsonInclude(JsonInclude.Include.NON_NULL) record Vci( @JsonProperty("credential_issuer_url") String credentialIssuerUrl, @JsonProperty("credential_configuration_id") String credentialConfigurationId, @@ -76,6 +91,9 @@ record VciSuiteConfig( @JsonProperty("status_list_trust_anchor_pem") String statusListTrustAnchorPem) { } - record Client(@JsonProperty("client_id") String clientId) { + @JsonInclude(JsonInclude.Include.NON_NULL) + record Client( + @JsonProperty("client_id") String clientId, + @JsonProperty("jwks") JsonNode jwks) { } } diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciTestSigningKey.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciTestSigningKey.java index d4a93f3b786..2c44cb6027d 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciTestSigningKey.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/VciTestSigningKey.java @@ -47,6 +47,8 @@ import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.REALM; + // Not using org.keycloak.common.util.CertificateUtils as its leaf certificates are CA capable, which strict // trust chain validation rejects for an issuer signing certificate final class VciTestSigningKey { @@ -79,7 +81,7 @@ final class VciTestSigningKey { new Certificate[] { leafCertificate, caCertificate }); Path keyStorePath = Files.createTempFile( - ConformanceSigningKey.realmKeystoreDir(VciConformanceRealmConfig.REALM), + ConformanceSigningKey.realmKeystoreDir(REALM), "keycloak-oid4vci-conformance-signing", ".p12"); try (OutputStream output = Files.newOutputStream(keyStorePath)) { keyStore.store(output, PASSWORD.toCharArray()); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/HaipVciConformanceRealmConfig.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/HaipVciConformanceRealmConfig.java new file mode 100644 index 00000000000..5c74fe4016a --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/HaipVciConformanceRealmConfig.java @@ -0,0 +1,182 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.haip; + +import java.util.List; +import java.util.Map; + +import org.keycloak.authentication.authenticators.client.AttestationBasedClientAuthenticator; +import org.keycloak.broker.trust.DefaultTrustIdentityProviderConfig; +import org.keycloak.broker.trust.DefaultTrustIdentityProviderFactory; +import org.keycloak.common.Profile; +import org.keycloak.constants.OID4VCIConstants; +import org.keycloak.json.RawJsonValue; +import org.keycloak.protocol.oidc.OIDCConfigAttributes; +import org.keycloak.representations.idm.ClientPolicyConditionRepresentation; +import org.keycloak.representations.idm.ClientPolicyExecutorRepresentation; +import org.keycloak.representations.idm.ClientPolicyRepresentation; +import org.keycloak.representations.idm.ClientProfileRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.services.clientpolicy.executor.PKCEEnforcerExecutorFactory; +import org.keycloak.services.clientpolicy.executor.RejectImplicitGrantExecutorFactory; +import org.keycloak.services.clientpolicy.executor.SecureParContentsExecutorFactory; +import org.keycloak.testframework.conformance.OpenIdConformanceServer; +import org.keycloak.testframework.realm.ClientBuilder; +import org.keycloak.testframework.realm.RealmBuilder; +import org.keycloak.testframework.realm.RealmConfig; +import org.keycloak.testframework.server.KeycloakServerConfig; +import org.keycloak.testframework.server.KeycloakServerConfigBuilder; +import org.keycloak.tests.conformance.ConformanceSigningKey; +import org.keycloak.tests.conformance.vci.VciAttesterKey; +import org.keycloak.tests.conformance.vci.VciConformanceRealmUtil; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; +import org.keycloak.util.JsonSerialization; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import static org.keycloak.OID4VCConstants.OID4VCI_ENABLED_ATTRIBUTE_KEY; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.CLIENT; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.CLIENT2; + +/** + * HAIP variant of the OID4VCI conformance realm. It composes the shared realm building blocks from + * {@link VciConformanceRealmUtil} and adds the High Assurance Interoperability Profile specifics: attestation-based + * client authentication with DPoP sender constraining, the client attestation trust anchors, and the HAIP client + * profile/policy (SecurePAR, no implicit grant, PKCE enforced). The non-HAIP variant lives in + * {@link NonHaipVciConformanceRealmConfig}. + */ +public class HaipVciConformanceRealmConfig implements RealmConfig { + + public static final String HAIP_PLAN = "oid4vci-1_0-issuer-haip-test-plan"; + + public static final String HAIP_CLIENT_PROFILE = "oid4vc-haip-profile"; + public static final String TRUST_IDP_ALIAS = "conformance-client-attester"; + public static final String X509_TRUST_IDP_ALIAS = "conformance-attester-x509"; + // The attester leaf certificate is generated with the emailProtection extended key usage. + private static final String ATTESTER_ATTESTATION_EKU = "1.3.6.1.5.5.7.3.4"; + + @Override + public RealmBuilder configure(RealmBuilder realm) { + VciConformanceRealmUtil.applyCommon(realm) + .clients(conformanceClient(CLIENT, false), conformanceClient(CLIENT2, true), VciConformanceRealmUtil.appClient()) + .clientProfile(haipClientProfile()) + .clientPolicy(haipClientPolicy()) + .update(rep -> { + VciConformanceRealmUtil.applyKeyProviders(rep); + rep.setIdentityProviders(List.of(attesterTrustIdentityProvider(), attesterX509TrustIdentityProvider())); + }); + return realm; + } + + // Attestation-based client authentication against the trust IDPs plus DPoP sender constraining. + private ClientBuilder conformanceClient(String clientId, boolean wildcardRedirect) { + return VciConformanceRealmUtil.baseConformanceClient(clientId, wildcardRedirect) + .authenticatorType(AttestationBasedClientAuthenticator.PROVIDER_ID) + .attribute(AttestationBasedClientAuthenticator.OAUTH_CLIENT_ATTESTATION_CONFIG_TRUST_IDPS, TRUST_IDP_ALIAS) + .attribute(OID4VCIConstants.OID4VCI_ATTESTER_TRUST_IDPS_ATTR, TRUST_IDP_ALIAS + "," + X509_TRUST_IDP_ALIAS) + // HAIP requires DPoP sender constraining, so the token endpoint must reject a request without a + // DPoP proof (the holder-of-key conformance module checks exactly this) + .attribute(OIDCConfigAttributes.DPOP_BOUND_ACCESS_TOKENS, "true"); + } + + private IdentityProviderRepresentation attesterTrustIdentityProvider() { + IdentityProviderRepresentation trust = new IdentityProviderRepresentation(); + trust.setAlias(TRUST_IDP_ALIAS); + trust.setProviderId(DefaultTrustIdentityProviderFactory.PROVIDER_ID); + trust.setEnabled(true); + trust.setConfig(Map.of(DefaultTrustIdentityProviderConfig.TRUSTED_JWKS, VciAttesterKey.publicJwks().toString())); + return trust; + } + + // Trusts the key attestation x5c certificate chain against the attester CA. Key attestations carry an x5c + // header, so Keycloak must be able to validate the chain against a configured X.509 trust domain. + private IdentityProviderRepresentation attesterX509TrustIdentityProvider() { + IdentityProviderRepresentation trust = new IdentityProviderRepresentation(); + trust.setAlias(X509_TRUST_IDP_ALIAS); + trust.setProviderId(DefaultTrustIdentityProviderFactory.PROVIDER_ID); + trust.setEnabled(true); + trust.setConfig(Map.of( + DefaultTrustIdentityProviderConfig.USE_X509, "true", + DefaultTrustIdentityProviderConfig.TRUSTED_CERTIFICATES, VciAttesterKey.caCertificatePem(), + DefaultTrustIdentityProviderConfig.REQUIRED_EXTENDED_KEY_USAGES, ATTESTER_ATTESTATION_EKU)); + return trust; + } + + private ClientProfileRepresentation haipClientProfile() { + ClientProfileRepresentation profile = new ClientProfileRepresentation(); + profile.setName(HAIP_CLIENT_PROFILE); + profile.setDescription("Enforces the OpenID4VC High Assurance Interoperability Profile 1.0"); + profile.setExecutors(List.of( + executor(SecureParContentsExecutorFactory.PROVIDER_ID, JsonNodeFactory.instance.objectNode()), + executor(RejectImplicitGrantExecutorFactory.PROVIDER_ID, + JsonNodeFactory.instance.objectNode().put("auto-configure", false)), + executor(PKCEEnforcerExecutorFactory.PROVIDER_ID, + JsonNodeFactory.instance.objectNode().put("auto-configure", false)))); + return profile; + } + + private ClientPolicyRepresentation haipClientPolicy() { + ClientPolicyRepresentation policy = new ClientPolicyRepresentation(); + policy.setName("oid4vc-haip-policy"); + policy.setDescription("Enables the oid4vc-haip-profile for OID4VCI clients"); + policy.setEnabled(true); + + ClientPolicyConditionRepresentation condition = new ClientPolicyConditionRepresentation(); + condition.setConditionProviderId("client-attributes"); + ObjectNode config = JsonNodeFactory.instance.objectNode(); + config.put("attributes", JsonSerialization.valueAsString(List.of(Map.of( + "key", OID4VCI_ENABLED_ATTRIBUTE_KEY, + "value", String.valueOf(true))))); + condition.setConfiguration(RawJsonValue.of(config)); + + policy.setConditions(List.of(condition)); + policy.setProfiles(List.of(HAIP_CLIENT_PROFILE)); + return policy; + } + + private ClientPolicyExecutorRepresentation executor(String providerId, JsonNode config) { + ClientPolicyExecutorRepresentation executor = new ClientPolicyExecutorRepresentation(); + executor.setExecutorProviderId(providerId); + executor.setConfiguration(RawJsonValue.of(config)); + return executor; + } + + public static class ConsentRequiredRealmConfig extends HaipVciConformanceRealmConfig { + + @Override + public RealmBuilder configure(RealmBuilder realm) { + return super.configure(realm).update(rep -> rep.getClients().stream() + .filter(client -> CLIENT.equals(client.getClientId()) || CLIENT2.equals(client.getClientId())) + .forEach(client -> client.setConsentRequired(true))); + } + } + + public static class ServerConfig implements KeycloakServerConfig { + + @Override + public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) { + return config.features(Profile.Feature.OID4VC_VCI, Profile.Feature.OID4VC_MDOC, Profile.Feature.CLIENT_AUTH_ABCA) + .option("https-protocols", VciConformanceRealmUtil.TLS_PROTOCOLS) + .option("https-cipher-suites", VciConformanceRealmUtil.BCP195_CIPHERS) + .option("hostname", OpenIdConformanceServer.KEYCLOAK_BASE_URI.toString()) + .spiOption("keys", "java-keystore", "keystores-path", ConformanceSigningKey.keystoresBaseDir()); + } + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerExpiredRequestUriTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerExpiredRequestUriTest.java similarity index 85% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerExpiredRequestUriTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerExpiredRequestUriTest.java index e88d02523d8..1427305c98f 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerExpiredRequestUriTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerExpiredRequestUriTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.fapi2; +package org.keycloak.tests.conformance.vci.haip.fapi2; import java.util.stream.Stream; @@ -29,9 +29,11 @@ import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.RealmBuilder; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerExpiredRequestUriTest extends AbstractVciConformanceTest { @InjectRealm(config = ShortParRequestUriLifespanRealmConfig.class, lifecycle = LifeCycle.METHOD) @@ -41,14 +43,14 @@ public class IssuerExpiredRequestUriTest extends AbstractVciConformanceTest { protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "fapi2-security-profile-final-par-attempt-to-use-expired-request_uri", ConformanceResult.REVIEW, // the expired request_uri is no longer resolvable, so Keycloak rejects it at the authorization endpoint BrowserInteraction.errorPage("PAR not found, not issued or used multiple times.")); } - public static class ShortParRequestUriLifespanRealmConfig extends VciConformanceRealmConfig { + public static class ShortParRequestUriLifespanRealmConfig extends HaipVciConformanceRealmConfig { @Override public RealmBuilder configure(RealmBuilder realm) { diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2LoginTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2LoginTest.java similarity index 88% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2LoginTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2LoginTest.java index 3ae4c072dad..9793af1b29f 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2LoginTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2LoginTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.fapi2; +package org.keycloak.tests.conformance.vci.haip.fapi2; import java.util.List; import java.util.stream.Stream; @@ -28,9 +28,11 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerFapi2LoginTest extends AbstractVciConformanceTest { private static final List MODULES = List.of( @@ -60,12 +62,12 @@ public class IssuerFapi2LoginTest extends AbstractVciConformanceTest { "fapi2-security-profile-final-plain-fapi-tolerate-unregistered-redirect-uri", "fapi2-security-profile-final-ensure-different-state-inside-and-outside-request-object"); - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { - return discoverModuleVariants(HAIP_PLAN, WALLET_INITIATED, MODULES, + return discoverModuleVariants(HAIP_PLAN, walletInitiated(), MODULES, ConformanceResult.PASSED, BrowserInteraction.LOGIN); } } diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2NoBrowserTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2NoBrowserTest.java similarity index 80% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2NoBrowserTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2NoBrowserTest.java index 58c06c1b4a5..e69ce13ee04 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2NoBrowserTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2NoBrowserTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.fapi2; +package org.keycloak.tests.conformance.vci.haip.fapi2; import java.util.List; import java.util.stream.Stream; @@ -28,9 +28,11 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerFapi2NoBrowserTest extends AbstractVciConformanceTest { private static final List MODULES = List.of( @@ -39,12 +41,12 @@ public class IssuerFapi2NoBrowserTest extends AbstractVciConformanceTest { "fapi2-security-profile-final-par-attempt-invalid-http-method", "fapi2-security-profile-final-ensure-request-object-without-redirect-uri-fails"); - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { - return discoverModuleVariants(HAIP_PLAN, WALLET_INITIATED, MODULES, + return discoverModuleVariants(HAIP_PLAN, walletInitiated(), MODULES, ConformanceResult.PASSED, BrowserInteraction.NONE); } } diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2PkceRejectedTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2PkceRejectedTest.java similarity index 80% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2PkceRejectedTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2PkceRejectedTest.java index 4839591c3c6..5a97387e83a 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2PkceRejectedTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2PkceRejectedTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.fapi2; +package org.keycloak.tests.conformance.vci.haip.fapi2; import java.util.List; import java.util.stream.Stream; @@ -28,25 +28,27 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; /** * Sends authorization requests with missing or plain PKCE: Keycloak rejects them at the authorization endpoint * and redirects the error to the callback without ever presenting a login, where the module validates it. */ -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerFapi2PkceRejectedTest extends AbstractVciConformanceTest { private static final List MODULES = List.of( "fapi2-security-profile-final-par-ensure-pkce-required", "fapi2-security-profile-final-par-plain-pkce-rejected"); - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { - return discoverModuleVariants(HAIP_PLAN, WALLET_INITIATED, MODULES, + return discoverModuleVariants(HAIP_PLAN, walletInitiated(), MODULES, ConformanceResult.PASSED, BrowserInteraction.ERROR_CALLBACK); } } diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2RequestUriForDifferentClientTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2RequestUriForDifferentClientTest.java similarity index 82% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2RequestUriForDifferentClientTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2RequestUriForDifferentClientTest.java index c65f43806ef..a2021bd1519 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2RequestUriForDifferentClientTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2RequestUriForDifferentClientTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.fapi2; +package org.keycloak.tests.conformance.vci.haip.fapi2; import java.util.stream.Stream; @@ -27,23 +27,25 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; /** * One client pushes a request, then a different client presents the same request_uri at the authorization * endpoint: Keycloak must reject it because the pushed request is bound to the client that created it. */ -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerFapi2RequestUriForDifferentClientTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "fapi2-security-profile-final-par-attempt-to-use-request_uri-for-different-client", ConformanceResult.REVIEW, // the second client's client_id does not match the one bound to the pushed request_uri diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2RequestUriReusePriorTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2RequestUriReusePriorTest.java similarity index 79% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2RequestUriReusePriorTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2RequestUriReusePriorTest.java index 93c4f9bfc0f..53b2b35a2c3 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2RequestUriReusePriorTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2RequestUriReusePriorTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.fapi2; +package org.keycloak.tests.conformance.vci.haip.fapi2; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerFapi2RequestUriReusePriorTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "fapi2-security-profile-final-par-ensure-reused-request-uri-prior-to-auth-completion-succeeds", ConformanceResult.PASSED, BrowserInteraction.LOGIN_ON_SECOND_VISIT); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2ReuseRequestUriTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2ReuseRequestUriTest.java similarity index 81% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2ReuseRequestUriTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2ReuseRequestUriTest.java index 9462a659d44..231abae2482 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2ReuseRequestUriTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2ReuseRequestUriTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.fapi2; +package org.keycloak.tests.conformance.vci.haip.fapi2; import java.util.stream.Stream; @@ -27,23 +27,25 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; /** * Uses a request_uri once (login completes), then reuses the same request_uri: Keycloak must reject the reuse * because the pushed request has been consumed. */ -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerFapi2ReuseRequestUriTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "fapi2-security-profile-final-par-attempt-reuse-request_uri", ConformanceResult.REVIEW, BrowserInteraction.loginThenErrorPageOnSecondVisit("PAR not found, not issued or used multiple times.")); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2StateOutsideRequestObjectTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2StateOutsideRequestObjectTest.java similarity index 81% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2StateOutsideRequestObjectTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2StateOutsideRequestObjectTest.java index 6996e786cb3..ae14cfc59d9 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2StateOutsideRequestObjectTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2StateOutsideRequestObjectTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.fapi2; +package org.keycloak.tests.conformance.vci.haip.fapi2; import java.util.stream.Stream; @@ -27,23 +27,25 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; /** * Sends an authorization request that carries state only outside the request object: Keycloak requires the * request to go through PAR, so it rejects the plain authorization request. */ -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerFapi2StateOutsideRequestObjectTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "fapi2-security-profile-final-state-only-outside-request-object-not-used", ConformanceResult.REVIEW, BrowserInteraction.errorPage("PAR request did not include query parameter")); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2UnsignedRequestWithoutParTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2UnsignedRequestWithoutParTest.java similarity index 81% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2UnsignedRequestWithoutParTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2UnsignedRequestWithoutParTest.java index bfb6ba3e65b..b747709c9e3 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerFapi2UnsignedRequestWithoutParTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerFapi2UnsignedRequestWithoutParTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.fapi2; +package org.keycloak.tests.conformance.vci.haip.fapi2; import java.util.stream.Stream; @@ -27,23 +27,25 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; /** * Sends an unsigned authorization request without going through PAR: Keycloak requires pushed authorization, so * it rejects the request because it has no pushed request_uri to resolve. */ -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerFapi2UnsignedRequestWithoutParTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "fapi2-security-profile-final-ensure-unsigned-authorization-request-without-using-par-fails", ConformanceResult.REVIEW, BrowserInteraction.errorPage("PAR request_uri not included.")); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerHolderOfKeyRequiredTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerHolderOfKeyRequiredTest.java similarity index 79% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerHolderOfKeyRequiredTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerHolderOfKeyRequiredTest.java index 25fcec82c22..8d33eeeb233 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerHolderOfKeyRequiredTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerHolderOfKeyRequiredTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.fapi2; +package org.keycloak.tests.conformance.vci.haip.fapi2; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerHolderOfKeyRequiredTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "fapi2-security-profile-final-ensure-holder-of-key-required", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerRefreshTokenTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerRefreshTokenTest.java similarity index 78% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerRefreshTokenTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerRefreshTokenTest.java index 856afc5fc66..7d55b709c9a 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerRefreshTokenTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerRefreshTokenTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.fapi2; +package org.keycloak.tests.conformance.vci.haip.fapi2; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerRefreshTokenTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "fapi2-security-profile-final-refresh-token", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerUserRejectsAuthenticationTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerUserRejectsAuthenticationTest.java similarity index 78% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerUserRejectsAuthenticationTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerUserRejectsAuthenticationTest.java index 9243a331f37..308ed0d7f96 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/fapi2/IssuerUserRejectsAuthenticationTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/fapi2/IssuerUserRejectsAuthenticationTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.fapi2; +package org.keycloak.tests.conformance.vci.haip.fapi2; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerUserRejectsAuthenticationTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.ConsentRequiredRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.ConsentRequiredRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "fapi2-security-profile-final-user-rejects-authentication", ConformanceResult.PASSED, BrowserInteraction.DENY_CONSENT); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerAccessTokenInQueryTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerAccessTokenInQueryTest.java similarity index 78% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerAccessTokenInQueryTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerAccessTokenInQueryTest.java index 7d95b677159..fe8de34942b 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerAccessTokenInQueryTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerAccessTokenInQueryTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerAccessTokenInQueryTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-on-access-token-in-query", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerBatchIssuanceTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerBatchIssuanceTest.java similarity index 82% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerBatchIssuanceTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerBatchIssuanceTest.java index 0f5c880e54f..0bd25f5b48c 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerBatchIssuanceTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerBatchIssuanceTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -29,9 +29,11 @@ import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.RealmBuilder; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerBatchIssuanceTest extends AbstractVciConformanceTest { @InjectRealm(config = BatchIssuanceRealmConfig.class, lifecycle = LifeCycle.METHOD) @@ -41,13 +43,13 @@ public class IssuerBatchIssuanceTest extends AbstractVciConformanceTest { protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-batch-issuance", ConformanceResult.PASSED, BrowserInteraction.LOGIN); } - public static class BatchIssuanceRealmConfig extends VciConformanceRealmConfig { + public static class BatchIssuanceRealmConfig extends HaipVciConformanceRealmConfig { @Override public RealmBuilder configure(RealmBuilder realm) { diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerClientAttestationExpInPastTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerClientAttestationExpInPastTest.java similarity index 79% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerClientAttestationExpInPastTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerClientAttestationExpInPastTest.java index a5415f0ef95..7e7a3db9fae 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerClientAttestationExpInPastTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerClientAttestationExpInPastTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerClientAttestationExpInPastTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-client-attestation-exp-in-past", ConformanceResult.PASSED, BrowserInteraction.NONE); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerClientAttestationNoSubTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerClientAttestationNoSubTest.java similarity index 78% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerClientAttestationNoSubTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerClientAttestationNoSubTest.java index 8ca1b88f558..bdc2693e077 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerClientAttestationNoSubTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerClientAttestationNoSubTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerClientAttestationNoSubTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-client-attestation-no-sub", ConformanceResult.PASSED, BrowserInteraction.NONE); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerClientAttestationPopWrongAudTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerClientAttestationPopWrongAudTest.java similarity index 79% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerClientAttestationPopWrongAudTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerClientAttestationPopWrongAudTest.java index 9e1333b698d..8570b513a94 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerClientAttestationPopWrongAudTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerClientAttestationPopWrongAudTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerClientAttestationPopWrongAudTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-client-attestation-pop-wrong-aud", ConformanceResult.PASSED, BrowserInteraction.NONE); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerHappyFlowAdditionalRequestsTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerHappyFlowAdditionalRequestsTest.java similarity index 79% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerHappyFlowAdditionalRequestsTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerHappyFlowAdditionalRequestsTest.java index 201cf708c1e..ad3b5601c9d 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerHappyFlowAdditionalRequestsTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerHappyFlowAdditionalRequestsTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerHappyFlowAdditionalRequestsTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-happy-flow-additional-requests", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerHappyFlowMultipleClientsTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerHappyFlowMultipleClientsTest.java similarity index 78% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerHappyFlowMultipleClientsTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerHappyFlowMultipleClientsTest.java index 56f75890746..7f519f63cb9 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerHappyFlowMultipleClientsTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerHappyFlowMultipleClientsTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerHappyFlowMultipleClientsTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-happy-flow-multiple-clients", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerHappyFlowSkipNotificationTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerHappyFlowSkipNotificationTest.java similarity index 78% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerHappyFlowSkipNotificationTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerHappyFlowSkipNotificationTest.java index 76d386c1ec3..5dae10cb57d 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerHappyFlowSkipNotificationTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerHappyFlowSkipNotificationTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerHappyFlowSkipNotificationTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-happy-flow-skip-notification", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerHappyFlowTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerHappyFlowTest.java similarity index 78% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerHappyFlowTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerHappyFlowTest.java index 47648f2555a..4e8db3a4f9c 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerHappyFlowTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerHappyFlowTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerHappyFlowTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-happy-flow", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInitiatedBatchIssuanceTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInitiatedBatchIssuanceTest.java similarity index 83% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInitiatedBatchIssuanceTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInitiatedBatchIssuanceTest.java index b0a7c808e03..46168c8c901 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInitiatedBatchIssuanceTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInitiatedBatchIssuanceTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,9 +27,11 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerInitiatedBatchIssuanceTest extends AbstractVciConformanceTest { @InjectRealm(config = IssuerBatchIssuanceTest.BatchIssuanceRealmConfig.class, lifecycle = LifeCycle.METHOD) @@ -39,7 +41,7 @@ public class IssuerInitiatedBatchIssuanceTest extends AbstractVciConformanceTest protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - ISSUER_INITIATED, + issuerInitiated(), "oid4vci-1_0-issuer-batch-issuance", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInitiatedHappyFlowTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInitiatedHappyFlowTest.java similarity index 84% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInitiatedHappyFlowTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInitiatedHappyFlowTest.java index 4e22928a1e4..d3ec9593aee 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInitiatedHappyFlowTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInitiatedHappyFlowTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,24 +27,26 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; /** * The issuer_initiated flow: Keycloak creates a credential offer through the verifiable_credential_offer * application initiated action and the suite receives it, fetches the offer from its credential_offer_uri and * completes the authorization code flow with its own wallet client. */ -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerInitiatedHappyFlowTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - ISSUER_INITIATED, + issuerInitiated(), "oid4vci-1_0-issuer-happy-flow", ConformanceResult.PASSED, BrowserInteraction.LOGIN) diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInitiatedRestOfferTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInitiatedRestOfferTest.java similarity index 82% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInitiatedRestOfferTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInitiatedRestOfferTest.java index 0f31a199335..04ac3d511fc 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInitiatedRestOfferTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInitiatedRestOfferTest.java @@ -15,25 +15,27 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; import org.junit.jupiter.api.Disabled; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.REALM; + // TODO: enable once https://github.com/keycloak/keycloak/issues/48188 is fixed. The REST endpoint binds the // offer to the client of the access token used to create it, and redeeming an offer with a different client is // rejected ("Unexpected login client"). The suite redeems with its own wallet client, so the offer must be // created without a target client, which only the AIA path (see IssuerInitiatedHappyFlowTest) supports today. @Disabled("The create-credential-offer REST endpoint binds the offer to the creating client, see the TODO") -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerInitiatedRestOfferTest extends IssuerInitiatedHappyFlowTest { @Override protected String createCredentialOfferUri() { throw new UnsupportedOperationException( - "Create the offer via POST /realms/" + VciConformanceRealmConfig.REALM + "Create the offer via POST /realms/" + REALM + "/protocol/oid4vc/create-credential-offer once offers are no longer bound to the creating client"); } } diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidClientAttestationPopSignatureTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidClientAttestationPopSignatureTest.java similarity index 79% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidClientAttestationPopSignatureTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidClientAttestationPopSignatureTest.java index 9b3d199ae73..b268af6c4db 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidClientAttestationPopSignatureTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidClientAttestationPopSignatureTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerInvalidClientAttestationPopSignatureTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-invalid-client-attestation-pop-signature", ConformanceResult.PASSED, BrowserInteraction.NONE); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidClientAttestationSignatureTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidClientAttestationSignatureTest.java similarity index 79% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidClientAttestationSignatureTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidClientAttestationSignatureTest.java index 01169105ed7..aea721756a9 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidClientAttestationSignatureTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidClientAttestationSignatureTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerInvalidClientAttestationSignatureTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-invalid-client-attestation-signature", ConformanceResult.PASSED, BrowserInteraction.NONE); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidJwtProofSignatureTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidJwtProofSignatureTest.java similarity index 79% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidJwtProofSignatureTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidJwtProofSignatureTest.java index 68eb85ae678..e9ebef95fc9 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidJwtProofSignatureTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidJwtProofSignatureTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerInvalidJwtProofSignatureTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-invalid-jwt-proof-signature", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidKeyAttestationSignatureTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidKeyAttestationSignatureTest.java similarity index 83% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidKeyAttestationSignatureTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidKeyAttestationSignatureTest.java index 1fe783c5ab0..25e025a6cc9 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidKeyAttestationSignatureTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidKeyAttestationSignatureTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,9 +27,11 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerInvalidKeyAttestationSignatureTest extends AbstractVciConformanceTest { @InjectRealm(config = IssuerKeyAttestationTest.KeyAttestationRequiredRealmConfig.class, lifecycle = LifeCycle.METHOD) @@ -39,7 +41,7 @@ public class IssuerInvalidKeyAttestationSignatureTest extends AbstractVciConform protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-invalid-key-attestation-signature", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidNonceTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidNonceTest.java similarity index 78% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidNonceTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidNonceTest.java index 8f980ada94c..9a4c7dee5e2 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerInvalidNonceTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerInvalidNonceTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerInvalidNonceTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-invalid-nonce", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerKeyAttestationTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerKeyAttestationTest.java similarity index 81% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerKeyAttestationTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerKeyAttestationTest.java index 1836849dc45..e6e23adfcfa 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerKeyAttestationTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerKeyAttestationTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -29,14 +29,17 @@ import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.RealmBuilder; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.SD_JWT_SCOPE; +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; /** * Issues a credential whose configuration requires key attestations, so the suite includes a valid key attestation * that Keycloak must accept. The attestation x5c chain is trusted through the {@code conformance-attester-x509} - * trust-material identity provider configured by {@link VciConformanceRealmConfig}. + * trust-material identity provider configured by {@link HaipVciConformanceRealmConfig}. */ -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerKeyAttestationTest extends AbstractVciConformanceTest { @InjectRealm(config = KeyAttestationRequiredRealmConfig.class, lifecycle = LifeCycle.METHOD) @@ -46,13 +49,13 @@ public class IssuerKeyAttestationTest extends AbstractVciConformanceTest { protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-happy-flow", ConformanceResult.PASSED, BrowserInteraction.LOGIN); } - public static class KeyAttestationRequiredRealmConfig extends VciConformanceRealmConfig { + public static class KeyAttestationRequiredRealmConfig extends HaipVciConformanceRealmConfig { @Override public RealmBuilder configure(RealmBuilder realm) { diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerMismatchedClientAttestationPopKeyTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerMismatchedClientAttestationPopKeyTest.java similarity index 79% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerMismatchedClientAttestationPopKeyTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerMismatchedClientAttestationPopKeyTest.java index 2cbd46fd289..88b5517b4b8 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerMismatchedClientAttestationPopKeyTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerMismatchedClientAttestationPopKeyTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerMismatchedClientAttestationPopKeyTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-mismatched-client-attestation-pop-key", ConformanceResult.PASSED, BrowserInteraction.NONE); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerMissingProofTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerMissingProofTest.java similarity index 78% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerMissingProofTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerMissingProofTest.java index 0a54e85f244..242c85f5d62 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerMissingProofTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerMissingProofTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerMissingProofTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-missing-proof", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerSignedMetadataTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerSignedMetadataTest.java similarity index 78% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerSignedMetadataTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerSignedMetadataTest.java index 5dd29d46411..450666faffd 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerSignedMetadataTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerSignedMetadataTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerSignedMetadataTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-metadata-test-signed", ConformanceResult.PASSED, BrowserInteraction.NONE); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerUnknownCredentialConfigurationTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerUnknownCredentialConfigurationTest.java similarity index 79% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerUnknownCredentialConfigurationTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerUnknownCredentialConfigurationTest.java index 3bf802b942e..c4c858d25b2 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerUnknownCredentialConfigurationTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerUnknownCredentialConfigurationTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerUnknownCredentialConfigurationTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-unknown-credential-configuration", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerUnknownCredentialIdentifierTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerUnknownCredentialIdentifierTest.java similarity index 79% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerUnknownCredentialIdentifierTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerUnknownCredentialIdentifierTest.java index 43f05a2254b..0b9bab65f81 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerUnknownCredentialIdentifierTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerUnknownCredentialIdentifierTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerUnknownCredentialIdentifierTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-unknown-credential-identifier", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerUnsignedMetadataTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerUnsignedMetadataTest.java similarity index 78% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerUnsignedMetadataTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerUnsignedMetadataTest.java index 87488e3152d..c52407d3577 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerUnsignedMetadataTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerUnsignedMetadataTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerUnsignedMetadataTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-metadata-test", ConformanceResult.PASSED, BrowserInteraction.NONE); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerUnsupportedEncryptionAlgorithmTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerUnsupportedEncryptionAlgorithmTest.java similarity index 79% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerUnsupportedEncryptionAlgorithmTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerUnsupportedEncryptionAlgorithmTest.java index 921123606e0..88670c0d449 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/issuer/IssuerUnsupportedEncryptionAlgorithmTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/issuer/IssuerUnsupportedEncryptionAlgorithmTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci.issuer; +package org.keycloak.tests.conformance.vci.haip.issuer; import java.util.stream.Stream; @@ -27,19 +27,21 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; -import org.keycloak.tests.conformance.vci.VciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; + +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerUnsupportedEncryptionAlgorithmTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { return discoverModuleVariants( HAIP_PLAN, - WALLET_INITIATED, + walletInitiated(), "oid4vci-1_0-issuer-fail-unsupported-encryption-algorithm", ConformanceResult.PASSED, BrowserInteraction.LOGIN); diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/IssuerMdocHappyFlowTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/mdoc/IssuerMdocHappyFlowTest.java similarity index 69% rename from tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/IssuerMdocHappyFlowTest.java rename to tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/mdoc/IssuerMdocHappyFlowTest.java index f7554014021..2301e7c5306 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/IssuerMdocHappyFlowTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/haip/mdoc/IssuerMdocHappyFlowTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.tests.conformance.vci; +package org.keycloak.tests.conformance.vci.haip.mdoc; import java.util.Map; import java.util.stream.Stream; @@ -27,23 +27,29 @@ import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; +import org.keycloak.tests.conformance.vci.haip.issuer.IssuerHappyFlowTest; + +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.MDOC_CREDENTIAL_FORMAT_VARIANT; +import static org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig.HAIP_PLAN; /** - * Runs the issuer happy flow with the ISO mdoc credential format, mirroring {@link org.keycloak.tests.conformance.vci.issuer.IssuerHappyFlowTest} for SD-JWT VC. + * Runs the issuer happy flow with the ISO mdoc credential format, mirroring {@link IssuerHappyFlowTest} for SD-JWT VC. */ -@KeycloakIntegrationTest(config = VciConformanceRealmConfig.ServerConfig.class) +@KeycloakIntegrationTest(config = HaipVciConformanceRealmConfig.ServerConfig.class) public class IssuerMdocHappyFlowTest extends AbstractVciConformanceTest { - @InjectRealm(config = VciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + @InjectRealm(config = HaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) ManagedRealm realm; @Override protected Stream moduleVariants() { // The plan variant pins every dimension but vci_credential_encryption, leaving the plain and encrypted variants return discoverModuleVariants( - "oid4vci-1_0-issuer-haip-test-plan", + HAIP_PLAN, Map.of( - "credential_format", VciConformanceRealmConfig.MDOC_CREDENTIAL_FORMAT_VARIANT, + "credential_format", MDOC_CREDENTIAL_FORMAT_VARIANT, "vci_authorization_code_flow_variant", "wallet_initiated"), "oid4vci-1_0-issuer-happy-flow", ConformanceResult.PASSED, diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/AbstractNonHaipVciConformanceTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/AbstractNonHaipVciConformanceTest.java new file mode 100644 index 00000000000..26dfb9cb3fd --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/AbstractNonHaipVciConformanceTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip; + +import java.util.HashMap; +import java.util.Map; + +import org.keycloak.tests.conformance.vci.AbstractVciConformanceTest; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Baseline for non-HAIP OID4VCI conformance tests. Non-HAIP profiles authenticate with private_key_jwt and use + * neither client attestation nor key attestation, so the suite config advertises the client's private JWKS instead + * of any attester JWKS. + * + * Unlike the HAIP plan, which pins every FAPI2/VCI variant dimension inside the plan, the non-HAIP plan + * ({@code oid4vci-1_0-issuer-test-plan}) pins nothing, so the caller must supply the full variant selection. This + * overrides {@link #planVariant(String)} to add the non-HAIP FAPI2 dimensions (profile {@code vci}, + * private_key_jwt, DPoP, unsigned PAR request, plain OAuth) on top of the credential format and flow variant. + * Credential encryption is intentionally omitted so it stays discovered per module (it defaults to {@code plain}). + */ +public abstract class AbstractNonHaipVciConformanceTest extends AbstractVciConformanceTest { + + @Override + protected Map planVariant(String flowVariant) { + return Map.of( + "credential_format", "sd_jwt_vc", + "vci_authorization_code_flow_variant", flowVariant, + "fapi_profile", "vci", + "client_auth_type", "private_key_jwt", + "fapi_request_method", "unsigned", + "openid", "plain_oauth", + "sender_constrain", "dpop", + "fapi_response_mode", "plain_response"); + } + + /** + * Adds {@code vci_credential_encryption=encrypted} to a plan variant. The generic non-HAIP plan does not pin + * credential encryption (it defaults to plain), so tests that must exercise the encrypted credential response + * request it explicitly. + */ + protected Map encrypted(Map planVariant) { + Map variant = new HashMap<>(planVariant); + variant.put("vci_credential_encryption", "encrypted"); + return variant; + } + + // Non-HAIP authenticates with private_key_jwt, so it advertises no client attestation material. + @Override + protected JsonNode clientAttesterJwks() { + return null; + } + + // Key attestation is off by default; the key-attestation tests re-enable it. + @Override + protected JsonNode keyAttestationJwks() { + return null; + } + + @Override + protected JsonNode clientJwks() { + return VciClientKey.privateJwks(); + } + + @Override + protected JsonNode client2Jwks() { + return VciClientKey.privateJwks2(); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/NonHaipVciConformanceRealmConfig.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/NonHaipVciConformanceRealmConfig.java new file mode 100644 index 00000000000..a7cc5c899eb --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/NonHaipVciConformanceRealmConfig.java @@ -0,0 +1,120 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip; + +import java.util.List; +import java.util.Map; + +import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; +import org.keycloak.broker.trust.DefaultTrustIdentityProviderConfig; +import org.keycloak.broker.trust.DefaultTrustIdentityProviderFactory; +import org.keycloak.common.Profile; +import org.keycloak.constants.OID4VCIConstants; +import org.keycloak.protocol.oidc.OIDCConfigAttributes; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.testframework.conformance.OpenIdConformanceServer; +import org.keycloak.testframework.realm.ClientBuilder; +import org.keycloak.testframework.realm.RealmBuilder; +import org.keycloak.testframework.realm.RealmConfig; +import org.keycloak.testframework.server.KeycloakServerConfig; +import org.keycloak.testframework.server.KeycloakServerConfigBuilder; +import org.keycloak.tests.conformance.ConformanceSigningKey; +import org.keycloak.tests.conformance.vci.VciAttesterKey; +import org.keycloak.tests.conformance.vci.VciConformanceRealmUtil; +import org.keycloak.tests.conformance.vci.haip.HaipVciConformanceRealmConfig; + +import com.fasterxml.jackson.databind.JsonNode; + +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.CLIENT; +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.CLIENT2; + +/** + * Non-HAIP variant of the OID4VCI conformance realm. It composes the shared realm building blocks from + * {@link VciConformanceRealmUtil} and, unlike {@link HaipVciConformanceRealmConfig}, authenticates the conformance + * clients with private_key_jwt (confidential clients holding a registered public JWKS) instead of attestation-based + * client authentication, so there are no client attestation trust anchors and no HAIP client profile/policy. + * + * The conformance suite's OID4VCI issuer test modules do not permit public clients or client_secret authentication + * ({@code client_auth_type=none}/{@code client_secret_*} are marked not applicable), so private_key_jwt is the + * non-HAIP client authentication used here. FAPI2 still mandates sender constraining, so the clients keep DPoP. + * + * Pairs with its own {@link ServerConfig}, the neutral OID4VC_VCI server baseline (no CLIENT_AUTH_ABCA), since + * non-HAIP does not use attestation-based client authentication. + */ +public class NonHaipVciConformanceRealmConfig implements RealmConfig { + + public static final String NON_HAIP_PLAN = "oid4vci-1_0-issuer-test-plan"; + + // Key attestations carry an x5c chain, so Keycloak validates them against this X.509 trust domain. Client + // attestation (HAIP) is not used here, only key attestation on the proof, so there is no JWKS trust IDP. + public static final String X509_TRUST_IDP_ALIAS = "conformance-attester-x509"; + // The attester leaf certificate is generated with the emailProtection extended key usage. + private static final String ATTESTER_ATTESTATION_EKU = "1.3.6.1.5.5.7.3.4"; + + @Override + public RealmBuilder configure(RealmBuilder realm) { + VciConformanceRealmUtil.applyCommon(realm) + .clients(conformanceClient(CLIENT, false, VciClientKey.publicJwks()), + conformanceClient(CLIENT2, true, VciClientKey.publicJwks2()), + VciConformanceRealmUtil.appClient()) + .update(rep -> { + VciConformanceRealmUtil.applyKeyProviders(rep); + rep.setIdentityProviders(List.of(attesterX509TrustIdentityProvider())); + }); + return realm; + } + + // Non-HAIP client authentication: confidential client using private_key_jwt with the conformance client's own + // public JWKS registered on the client, plus DPoP sender constraining as FAPI2 requires. The suite holds the + // matching private JWKS (see VciClientKey / VciSuiteConfig) to sign the client assertions. The attester trust + // IDP is referenced so key attestations on the proof can be validated. + private ClientBuilder conformanceClient(String clientId, boolean wildcardRedirect, JsonNode publicJwks) { + return VciConformanceRealmUtil.baseConformanceClient(clientId, wildcardRedirect) + .authenticatorType(JWTClientAuthenticator.PROVIDER_ID) + .attribute(OIDCConfigAttributes.USE_JWKS_STRING, "true") + .attribute(OIDCConfigAttributes.JWKS_STRING, publicJwks.toString()) + .attribute(OID4VCIConstants.OID4VCI_ATTESTER_TRUST_IDPS_ATTR, X509_TRUST_IDP_ALIAS) + .attribute(OIDCConfigAttributes.DPOP_BOUND_ACCESS_TOKENS, "true"); + } + + // Trusts the key attestation x5c certificate chain against the attester CA, so Keycloak can validate the chain + // against a configured X.509 trust domain when a credential configuration requires key attestation. + private IdentityProviderRepresentation attesterX509TrustIdentityProvider() { + IdentityProviderRepresentation trust = new IdentityProviderRepresentation(); + trust.setAlias(X509_TRUST_IDP_ALIAS); + trust.setProviderId(DefaultTrustIdentityProviderFactory.PROVIDER_ID); + trust.setEnabled(true); + trust.setConfig(Map.of( + DefaultTrustIdentityProviderConfig.USE_X509, "true", + DefaultTrustIdentityProviderConfig.TRUSTED_CERTIFICATES, VciAttesterKey.caCertificatePem(), + DefaultTrustIdentityProviderConfig.REQUIRED_EXTENDED_KEY_USAGES, ATTESTER_ATTESTATION_EKU)); + return trust; + } + + public static class ServerConfig implements KeycloakServerConfig { + + @Override + public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) { + return config.features(Profile.Feature.OID4VC_VCI) + .option("https-protocols", VciConformanceRealmUtil.TLS_PROTOCOLS) + .option("https-cipher-suites", VciConformanceRealmUtil.BCP195_CIPHERS) + .option("hostname", OpenIdConformanceServer.KEYCLOAK_BASE_URI.toString()) + .spiOption("keys", "java-keystore", "keystores-path", ConformanceSigningKey.keystoresBaseDir()); + } + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/VciClientKey.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/VciClientKey.java new file mode 100644 index 00000000000..6329fe0d140 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/VciClientKey.java @@ -0,0 +1,66 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip; + +import org.keycloak.tests.conformance.ConformanceSigningKey; +import org.keycloak.tests.conformance.vci.VciAttesterKey; + +import com.fasterxml.jackson.databind.JsonNode; +import org.bouncycastle.asn1.x509.KeyPurposeId; + +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.REALM; + +/** + * The client signing key for non-HAIP private_key_jwt client authentication, generated at runtime so no private + * key material is committed to the repository. The conformance suite (acting as the wallet client) holds the + * private JWKS to sign client assertions, while Keycloak registers only the public JWKS on the client. Unlike + * {@link VciAttesterKey} there is no x5c chain validation for private_key_jwt: Keycloak verifies the assertion + * signature against the registered public JWKS, so the leaf certificate extended key usage is immaterial here. + */ +public final class VciClientKey { + + // Two independent keys, one per conformance client. The suite's multiple-clients module asserts the two + // clients authenticate with different private keys (ValidateClientPrivateKeysAreDifferent), so the keys must + // not be shared. + private static final ConformanceSigningKey KEY = ConformanceSigningKey.generate( + REALM, "ct_client_signing_key", "OID4VCI Conformance Client", + KeyPurposeId.id_kp_clientAuth); + + private static final ConformanceSigningKey KEY2 = ConformanceSigningKey.generate( + REALM, "ct_client2_signing_key", "OID4VCI Conformance Client 2", + KeyPurposeId.id_kp_clientAuth); + + private VciClientKey() { + } + + static JsonNode privateJwks() { + return KEY.privateJwks(); + } + + static JsonNode publicJwks() { + return KEY.publicJwks(); + } + + static JsonNode privateJwks2() { + return KEY2.privateJwks(); + } + + static JsonNode publicJwks2() { + return KEY2.publicJwks(); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerAccessTokenInQueryTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerAccessTokenInQueryTest.java new file mode 100644 index 00000000000..85ffd854672 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerAccessTokenInQueryTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerAccessTokenInQueryTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-fail-on-access-token-in-query", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerBatchIssuanceTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerBatchIssuanceTest.java new file mode 100644 index 00000000000..1dc0ac84a19 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerBatchIssuanceTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.constants.OID4VCIConstants; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.realm.RealmBuilder; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerBatchIssuanceTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = BatchIssuanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-batch-issuance", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } + + public static class BatchIssuanceRealmConfig extends NonHaipVciConformanceRealmConfig { + + @Override + public RealmBuilder configure(RealmBuilder realm) { + return super.configure(realm).attribute(OID4VCIConstants.BATCH_CREDENTIAL_ISSUANCE_BATCH_SIZE, "20"); + } + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerHappyFlowAdditionalRequestsTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerHappyFlowAdditionalRequestsTest.java new file mode 100644 index 00000000000..0dcae9e11fe --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerHappyFlowAdditionalRequestsTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerHappyFlowAdditionalRequestsTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-happy-flow-additional-requests", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerHappyFlowMultipleClientsTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerHappyFlowMultipleClientsTest.java new file mode 100644 index 00000000000..4bd636df239 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerHappyFlowMultipleClientsTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerHappyFlowMultipleClientsTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-happy-flow-multiple-clients", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerHappyFlowSkipNotificationTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerHappyFlowSkipNotificationTest.java new file mode 100644 index 00000000000..94e0b575d7a --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerHappyFlowSkipNotificationTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerHappyFlowSkipNotificationTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-happy-flow-skip-notification", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerHappyFlowTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerHappyFlowTest.java new file mode 100644 index 00000000000..22dc8b6f2f3 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerHappyFlowTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerHappyFlowTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-happy-flow", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInitiatedBatchIssuanceTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInitiatedBatchIssuanceTest.java new file mode 100644 index 00000000000..c43a8dcde91 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInitiatedBatchIssuanceTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerInitiatedBatchIssuanceTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = IssuerBatchIssuanceTest.BatchIssuanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + issuerInitiated(), + "oid4vci-1_0-issuer-batch-issuance", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInitiatedHappyFlowTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInitiatedHappyFlowTest.java new file mode 100644 index 00000000000..a82417d427a --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInitiatedHappyFlowTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +/** + * The issuer_initiated flow: Keycloak creates a credential offer through the verifiable_credential_offer + * application initiated action and the suite receives it, fetches the offer from its credential_offer_uri and + * completes the authorization code flow with its own wallet client. + */ +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerInitiatedHappyFlowTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + issuerInitiated(), + "oid4vci-1_0-issuer-happy-flow", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN) + // TODO (#50889): include the encrypted variant once Keycloak keeps the credential offer state for the + // lifetime of the authorized session. Keycloak removes the offer state after the first + // successful issuance (OID4VCIssuerEndpoint), so the encrypted variant's second credential + // request (encryption + DEFLATE compression check) fails with "No credential offer state". + // The generic non-HAIP plan does not pin credential encryption, so the discovered variant may + // omit the key entirely (defaults to plain); exclude only the explicit encrypted variant. + .filter(module -> !"encrypted".equals(module.moduleVariant().get("vci_credential_encryption"))); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInitiatedRestOfferTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInitiatedRestOfferTest.java new file mode 100644 index 00000000000..f8a3e29333a --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInitiatedRestOfferTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import org.junit.jupiter.api.Disabled; + +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.REALM; + +// TODO: enable once https://github.com/keycloak/keycloak/issues/48188 is fixed. The REST endpoint binds the +// offer to the client of the access token used to create it, and redeeming an offer with a different client is +// rejected ("Unexpected login client"). The suite redeems with its own wallet client, so the offer must be +// created without a target client, which only the AIA path (see IssuerInitiatedHappyFlowTest) supports today. +@Disabled("The create-credential-offer REST endpoint binds the offer to the creating client, see the TODO") +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerInitiatedRestOfferTest extends IssuerInitiatedHappyFlowTest { + + @Override + protected String createCredentialOfferUri() { + throw new UnsupportedOperationException( + "Create the offer via POST /realms/" + REALM + + "/protocol/oid4vc/create-credential-offer once offers are no longer bound to the creating client"); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInvalidJwtProofSignatureTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInvalidJwtProofSignatureTest.java new file mode 100644 index 00000000000..f7fe9eff5a5 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInvalidJwtProofSignatureTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerInvalidJwtProofSignatureTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-fail-invalid-jwt-proof-signature", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInvalidKeyAttestationSignatureTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInvalidKeyAttestationSignatureTest.java new file mode 100644 index 00000000000..a395e64e43f --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInvalidKeyAttestationSignatureTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.VciConformanceRealmUtil; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import com.fasterxml.jackson.databind.JsonNode; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerInvalidKeyAttestationSignatureTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = IssuerKeyAttestationTest.KeyAttestationRequiredRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-fail-invalid-key-attestation-signature", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } + + // Non-HAIP does not use client attestation, but this test exercises key attestation on the credential, + // so the suite still needs the key attestation JWKS advertised (client attestation stays disabled). + @Override + protected JsonNode keyAttestationJwks() { + return VciConformanceRealmUtil.attesterJwks(); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInvalidNonceTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInvalidNonceTest.java new file mode 100644 index 00000000000..20c8f592b46 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerInvalidNonceTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerInvalidNonceTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-fail-invalid-nonce", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerKeyAttestationTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerKeyAttestationTest.java new file mode 100644 index 00000000000..9fe65552c90 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerKeyAttestationTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.models.oid4vci.CredentialScopeModel; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.realm.RealmBuilder; +import org.keycloak.tests.conformance.vci.VciConformanceRealmUtil; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import com.fasterxml.jackson.databind.JsonNode; + +import static org.keycloak.tests.conformance.vci.VciConformanceRealmUtil.SD_JWT_SCOPE; +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +/** + * Issues a credential whose configuration requires key attestations, so the suite includes a valid key attestation + * that Keycloak must accept. The attestation x5c chain is trusted through the {@code conformance-attester-x509} + * trust-material identity provider configured by {@link NonHaipVciConformanceRealmConfig}. + */ +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerKeyAttestationTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = KeyAttestationRequiredRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-happy-flow", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } + + // Non-HAIP does not use client attestation, but this test requires a key attestation on the credential, + // so the suite still needs the key attestation JWKS advertised (client attestation stays disabled). + @Override + protected JsonNode keyAttestationJwks() { + return VciConformanceRealmUtil.attesterJwks(); + } + + public static class KeyAttestationRequiredRealmConfig extends NonHaipVciConformanceRealmConfig { + + @Override + public RealmBuilder configure(RealmBuilder realm) { + return super.configure(realm).update(rep -> rep.getClientScopes().stream() + .filter(scope -> SD_JWT_SCOPE.equals(scope.getName())) + .forEach(scope -> scope.getAttributes().put(CredentialScopeModel.VC_KEY_ATTESTATION_REQUIRED, "true"))); + } + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerMissingProofTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerMissingProofTest.java new file mode 100644 index 00000000000..fdf05fb6a5e --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerMissingProofTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerMissingProofTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-fail-missing-proof", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerSignedMetadataTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerSignedMetadataTest.java new file mode 100644 index 00000000000..0692f7d6d52 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerSignedMetadataTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerSignedMetadataTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-metadata-test-signed", + ConformanceResult.PASSED, + BrowserInteraction.NONE); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerUnknownCredentialConfigurationTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerUnknownCredentialConfigurationTest.java new file mode 100644 index 00000000000..9033c85f3c7 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerUnknownCredentialConfigurationTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerUnknownCredentialConfigurationTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-fail-unknown-credential-configuration", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerUnknownCredentialIdentifierTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerUnknownCredentialIdentifierTest.java new file mode 100644 index 00000000000..6f8e3bb3f52 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerUnknownCredentialIdentifierTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerUnknownCredentialIdentifierTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-fail-unknown-credential-identifier", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerUnsignedMetadataTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerUnsignedMetadataTest.java new file mode 100644 index 00000000000..db667c9cb10 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerUnsignedMetadataTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerUnsignedMetadataTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + walletInitiated(), + "oid4vci-1_0-issuer-metadata-test", + ConformanceResult.PASSED, + BrowserInteraction.NONE); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerUnsupportedEncryptionAlgorithmTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerUnsupportedEncryptionAlgorithmTest.java new file mode 100644 index 00000000000..baba8f49436 --- /dev/null +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vci/nonhaip/issuer/IssuerUnsupportedEncryptionAlgorithmTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2026 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.tests.conformance.vci.nonhaip.issuer; + +import java.util.stream.Stream; + +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.conformance.runner.BrowserInteraction; +import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; +import org.keycloak.testframework.conformance.runner.ConformanceResult; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.tests.conformance.vci.nonhaip.AbstractNonHaipVciConformanceTest; +import org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig; + +import static org.keycloak.tests.conformance.vci.nonhaip.NonHaipVciConformanceRealmConfig.NON_HAIP_PLAN; + +@KeycloakIntegrationTest(config = NonHaipVciConformanceRealmConfig.ServerConfig.class) +public class IssuerUnsupportedEncryptionAlgorithmTest extends AbstractNonHaipVciConformanceTest { + + @InjectRealm(config = NonHaipVciConformanceRealmConfig.class, lifecycle = LifeCycle.METHOD) + ManagedRealm realm; + + @Override + protected Stream moduleVariants() { + return discoverModuleVariants( + NON_HAIP_PLAN, + // This module only runs when a credential response encryption is requested; the generic non-HAIP + // plan does not pin encryption (defaults to plain), so request the encrypted variant explicitly. + encrypted(walletInitiated()), + "oid4vci-1_0-issuer-fail-unsupported-encryption-algorithm", + ConformanceResult.PASSED, + BrowserInteraction.LOGIN); + } +} diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/AbstractVpConformanceTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/AbstractVpConformanceTest.java index 3abd8e0d421..5e1f96d2ab5 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/AbstractVpConformanceTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/AbstractVpConformanceTest.java @@ -55,7 +55,7 @@ abstract class AbstractVpConformanceTest extends AbstractConformanceTest { protected Consumer interaction(ConformanceModuleVariant moduleVariant) { return moduleRun -> { KeycloakVerifierBrowser browser = - new KeycloakVerifierBrowser(suite, keycloakUrls.getBase(), browserSslContext()); + new KeycloakVerifierBrowser(server, keycloakUrls.getBase(), browserSslContext()); if (crossDevice()) { browser.loginCrossDevice(VpConformanceRealmConfig.REALM, VpConformanceRealmConfig.CLIENT_ID, VpConformanceRealmConfig.IDP_ALIAS, moduleRun); @@ -77,7 +77,7 @@ abstract class AbstractVpConformanceTest extends AbstractConformanceTest { try { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, null); - trustStore.setCertificateEntry("conformance-suite", suite.nginxCertificate()); + trustStore.setCertificateEntry("conformance-suite", server.nginxCertificate()); KeyStore serverKeyStore = KeyStore.getInstance(certificates.getKeystoreFormat().name()); try (InputStream in = Files.newInputStream(Path.of(certificates.getServerKeyStorePath()))) { diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/KeycloakVerifierBrowser.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/KeycloakVerifierBrowser.java index 638f30c5bd5..827baeab816 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/KeycloakVerifierBrowser.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/KeycloakVerifierBrowser.java @@ -36,7 +36,7 @@ import org.keycloak.broker.oid4vp.OID4VPIdentityProviderEndpoint; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.UriUtils; import org.keycloak.protocol.oidc.utils.PkceUtils; -import org.keycloak.testframework.conformance.OpenIdConformanceSuite; +import org.keycloak.testframework.conformance.OpenIdConformanceServer; import org.keycloak.testframework.conformance.runner.ModuleRun; import org.keycloak.util.JsonSerialization; @@ -57,11 +57,11 @@ final class KeycloakVerifierBrowser { private static final Duration STATUS_POLL_TIMEOUT = Duration.ofSeconds(30); private static final Duration STATUS_POLL_INTERVAL = Duration.ofMillis(500); - private final OpenIdConformanceSuite suite; + private final OpenIdConformanceServer suite; private final String keycloakLocalBaseUrl; private final HttpClient httpClient; - KeycloakVerifierBrowser(OpenIdConformanceSuite suite, String keycloakLocalBaseUrl, SSLContext sslContext) { + KeycloakVerifierBrowser(OpenIdConformanceServer suite, String keycloakLocalBaseUrl, SSLContext sslContext) { this.suite = suite; this.keycloakLocalBaseUrl = keycloakLocalBaseUrl; this.httpClient = HttpClient.newBuilder() @@ -93,7 +93,7 @@ final class KeycloakVerifierBrowser { + "?client_id=" + urlEncode(clientId) + "&response_type=code" + "&scope=openid" - + "&redirect_uri=" + urlEncode(OpenIdConformanceSuite.KEYCLOAK_BASE_URI + "/callback") + + "&redirect_uri=" + urlEncode(OpenIdConformanceServer.KEYCLOAK_BASE_URI + "/callback") + "&code_challenge=" + urlEncode(PkceUtils.generateS256CodeChallenge(codeVerifier)) + "&code_challenge_method=S256" + "&kc_idp_hint=" + urlEncode(idpAlias); @@ -209,7 +209,7 @@ final class KeycloakVerifierBrowser { } private URI rewriteToHostReachable(URI uri) { - if (OpenIdConformanceSuite.KEYCLOAK_BASE_URI.getHost().equals(uri.getHost())) { + if (OpenIdConformanceServer.KEYCLOAK_BASE_URI.getHost().equals(uri.getHost())) { return URI.create(keycloakLocalBaseUrl + uri.getRawPath() + (uri.getRawQuery() != null ? "?" + uri.getRawQuery() : "")); } diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierCrossDeviceHappyFlowTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierCrossDeviceHappyFlowTest.java index 1d2e88f61c8..e5d428ec645 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierCrossDeviceHappyFlowTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierCrossDeviceHappyFlowTest.java @@ -28,6 +28,8 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier accepts a valid cross device SD-JWT VC presentation. The wallet uses the QR code link, * receives an empty JSON object from direct_post, and the browser finishes the login through the @@ -47,7 +49,7 @@ public class VerifierCrossDeviceHappyFlowTest extends AbstractVpConformanceTest @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierEncryptedInvalidKbJwtNonceTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierEncryptedInvalidKbJwtNonceTest.java index 130ddfd7692..8024bc76a7d 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierEncryptedInvalidKbJwtNonceTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierEncryptedInvalidKbJwtNonceTest.java @@ -26,6 +26,8 @@ import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier rejects a key binding JWT carrying the wrong nonce, presented as an encrypted * {@code direct_post.jwt} response. @@ -39,7 +41,7 @@ public class VerifierEncryptedInvalidKbJwtNonceTest extends AbstractVpConformanc @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierEncryptedSameDeviceHappyFlowTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierEncryptedSameDeviceHappyFlowTest.java index c6abd5e4f67..7f5837f5e38 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierEncryptedSameDeviceHappyFlowTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierEncryptedSameDeviceHappyFlowTest.java @@ -28,6 +28,8 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier accepts a valid same device SD-JWT VC presentation returned as an encrypted * {@code direct_post.jwt} response. @@ -41,7 +43,7 @@ public class VerifierEncryptedSameDeviceHappyFlowTest extends AbstractVpConforma @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidCredentialSignatureTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidCredentialSignatureTest.java index b76f2abf18c..069fd9b6643 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidCredentialSignatureTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidCredentialSignatureTest.java @@ -26,6 +26,8 @@ import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier rejects a credential with an invalid issuer signature. */ @@ -38,7 +40,7 @@ public class VerifierInvalidCredentialSignatureTest extends AbstractVpConformanc @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidKbJwtAudTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidKbJwtAudTest.java index 09e02eee02a..ca6e10f67fd 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidKbJwtAudTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidKbJwtAudTest.java @@ -26,6 +26,8 @@ import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier rejects a key binding JWT carrying the wrong audience. */ @@ -38,7 +40,7 @@ public class VerifierInvalidKbJwtAudTest extends AbstractVpConformanceTest { @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidKbJwtNonceTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidKbJwtNonceTest.java index 1afa0a74555..6efb2ec97c7 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidKbJwtNonceTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidKbJwtNonceTest.java @@ -26,6 +26,8 @@ import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier rejects a key binding JWT carrying the wrong nonce. */ @@ -38,7 +40,7 @@ public class VerifierInvalidKbJwtNonceTest extends AbstractVpConformanceTest { @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidKbJwtSignatureTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidKbJwtSignatureTest.java index 2425c407844..56fda0e760b 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidKbJwtSignatureTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidKbJwtSignatureTest.java @@ -26,6 +26,8 @@ import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier rejects a key binding JWT with an invalid signature. */ @@ -38,7 +40,7 @@ public class VerifierInvalidKbJwtSignatureTest extends AbstractVpConformanceTest @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidSdHashTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidSdHashTest.java index bedfb260891..29a81227bd2 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidSdHashTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierInvalidSdHashTest.java @@ -26,6 +26,8 @@ import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier rejects a key binding JWT whose sd_hash does not match the presentation. */ @@ -38,7 +40,7 @@ public class VerifierInvalidSdHashTest extends AbstractVpConformanceTest { @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierKbJwtIatInFutureTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierKbJwtIatInFutureTest.java index fc157753744..f9f2cf80618 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierKbJwtIatInFutureTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierKbJwtIatInFutureTest.java @@ -26,6 +26,8 @@ import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier rejects a key binding JWT issued in the future. */ @@ -38,7 +40,7 @@ public class VerifierKbJwtIatInFutureTest extends AbstractVpConformanceTest { @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierKbJwtIatInPastTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierKbJwtIatInPastTest.java index 68e2cd22839..a89aa10587a 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierKbJwtIatInPastTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierKbJwtIatInPastTest.java @@ -26,6 +26,8 @@ import org.keycloak.testframework.conformance.runner.ConformanceModuleVariant; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier rejects a key binding JWT issued too far in the past. */ @@ -38,7 +40,7 @@ public class VerifierKbJwtIatInPastTest extends AbstractVpConformanceTest { @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierMinimalCnfJwkTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierMinimalCnfJwkTest.java index 7ecbb3a6568..7d7d3ec4978 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierMinimalCnfJwkTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierMinimalCnfJwkTest.java @@ -28,6 +28,8 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier accepts a presentation whose credential carries a minimal holder cnf jwk. */ @@ -40,7 +42,7 @@ public class VerifierMinimalCnfJwkTest extends AbstractVpConformanceTest { @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierRequestUriMethodPostTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierRequestUriMethodPostTest.java index ab533258dff..3c39a5c797c 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierRequestUriMethodPostTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierRequestUriMethodPostTest.java @@ -30,6 +30,8 @@ import org.keycloak.testframework.realm.ManagedRealm; import org.junit.jupiter.api.Disabled; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier serves the authorization request object when the wallet retrieves request_uri through * POST. @@ -46,7 +48,7 @@ public class VerifierRequestUriMethodPostTest extends AbstractVpConformanceTest @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierSameDeviceHappyFlowTest.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierSameDeviceHappyFlowTest.java index 916ec37b6e7..d7c9408c9ff 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierSameDeviceHappyFlowTest.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VerifierSameDeviceHappyFlowTest.java @@ -28,6 +28,8 @@ import org.keycloak.testframework.conformance.runner.ConformanceResult; import org.keycloak.testframework.injection.LifeCycle; import org.keycloak.testframework.realm.ManagedRealm; +import static org.keycloak.tests.conformance.vp.VpConformanceRealmConfig.TEST_PLAN; + /** * The verifier accepts a valid same device SD-JWT VC presentation. */ @@ -40,7 +42,7 @@ public class VerifierSameDeviceHappyFlowTest extends AbstractVpConformanceTest { @Override protected Stream moduleVariants() { return discoverModuleVariants( - "oid4vp-1final-verifier-test-plan", + TEST_PLAN, Map.of( "vp_profile", "plain_vp", "credential_format", "sd_jwt_vc", diff --git a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VpConformanceRealmConfig.java b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VpConformanceRealmConfig.java index 9250911c224..6e5ad093fe9 100644 --- a/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VpConformanceRealmConfig.java +++ b/tests/conformance/src/test/java/org/keycloak/tests/conformance/vp/VpConformanceRealmConfig.java @@ -34,7 +34,7 @@ import org.keycloak.keys.JavaKeystoreKeyProviderFactory; import org.keycloak.keys.KeyProvider; import org.keycloak.representations.idm.ComponentExportRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; -import org.keycloak.testframework.conformance.OpenIdConformanceSuite; +import org.keycloak.testframework.conformance.OpenIdConformanceServer; import org.keycloak.testframework.realm.ClientBuilder; import org.keycloak.testframework.realm.RealmBuilder; import org.keycloak.testframework.realm.RealmConfig; @@ -44,6 +44,7 @@ import org.keycloak.tests.conformance.ConformanceSigningKey; public class VpConformanceRealmConfig implements RealmConfig { + public static final String TEST_PLAN = "oid4vp-1final-verifier-test-plan"; public static final String REALM = "oid4vp-verifier"; public static final String CLIENT_ID = "wallet-mock"; public static final String IDP_ALIAS = "oid4vp"; @@ -134,7 +135,7 @@ public class VpConformanceRealmConfig implements RealmConfig { @Override public KeycloakServerConfigBuilder configure(KeycloakServerConfigBuilder config) { return config.features(Profile.Feature.OID4VC_VP) - .option("hostname", OpenIdConformanceSuite.KEYCLOAK_BASE_URI.toString()) + .option("hostname", OpenIdConformanceServer.KEYCLOAK_BASE_URI.toString()) .spiOption("keys", "java-keystore", "keystores-path", ConformanceSigningKey.keystoresBaseDir()); } }