mirror of
https://github.com/keycloak/keycloak.git
synced 2026-10-23 19:29:23 +03:00
[OID4VCI] Conformance Non Haip configuration and execution. (#52271)
Closes #51389 Signed-off-by: Lukas Hanusovsky <lhanusov@redhat.com>
This commit is contained in:
9
.github/workflows/conformance.yml
vendored
9
.github/workflows/conformance.yml
vendored
@@ -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 }}
|
||||
|
||||
@@ -26,6 +26,6 @@ public class ConformanceTestFrameworkExtension implements TestFrameworkExtension
|
||||
|
||||
@Override
|
||||
public List<Supplier<?, ?>> suppliers() {
|
||||
return List.of(new OpenIdConformanceSuiteSupplier());
|
||||
return List.of(new OpenIdConformanceServerSupplier());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<OpenIdConformanceSuite, InjectConformanceSuite> {
|
||||
public class OpenIdConformanceServerSupplier implements Supplier<OpenIdConformanceServer, InjectOpenIdConformanceServer> {
|
||||
|
||||
@Override
|
||||
public OpenIdConformanceSuite getValue(InstanceContext<OpenIdConformanceSuite, InjectConformanceSuite> instanceContext) {
|
||||
return OpenIdConformanceSuite.instance();
|
||||
public OpenIdConformanceServer getValue(InstanceContext<OpenIdConformanceServer, InjectOpenIdConformanceServer> instanceContext) {
|
||||
return OpenIdConformanceServer.instance();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -37,13 +37,13 @@ public class OpenIdConformanceSuiteSupplier implements Supplier<OpenIdConformanc
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compatible(InstanceContext<OpenIdConformanceSuite, InjectConformanceSuite> a,
|
||||
RequestedInstance<OpenIdConformanceSuite, InjectConformanceSuite> b) {
|
||||
public boolean compatible(InstanceContext<OpenIdConformanceServer, InjectOpenIdConformanceServer> a,
|
||||
RequestedInstance<OpenIdConformanceServer, InjectOpenIdConformanceServer> b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(InstanceContext<OpenIdConformanceSuite, InjectConformanceSuite> instanceContext) {
|
||||
public void close(InstanceContext<OpenIdConformanceServer, InjectOpenIdConformanceServer> instanceContext) {
|
||||
instanceContext.getValue().close();
|
||||
}
|
||||
}
|
||||
@@ -24,5 +24,5 @@ import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface InjectConformanceSuite {
|
||||
public @interface InjectOpenIdConformanceServer {
|
||||
}
|
||||
67
tests/conformance/README.md
Normal file
67
tests/conformance/README.md
Normal file
@@ -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.
|
||||
@@ -52,5 +52,10 @@
|
||||
<groupId>org.keycloak.testframework</groupId>
|
||||
<artifactId>keycloak-test-framework-ui</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-suite</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -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<ConformanceModuleVariant> 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<String> names, ConformanceResult expectedResult, BrowserInteraction browserInteraction) {
|
||||
ConformanceModuleVariant template = new ConformanceModuleVariant(plan, planVariant, names.get(0), Map.of(),
|
||||
expectedResult, browserInteraction);
|
||||
Map<String, List<Map<String, String>>> discovered = OpenIdConformanceSuite.instance().client()
|
||||
Map<String, List<Map<String, String>>> 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());
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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<String, String> 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<String, String> WALLET_INITIATED = Map.of(
|
||||
"credential_format", "sd_jwt_vc",
|
||||
"vci_authorization_code_flow_variant", "wallet_initiated");
|
||||
protected Map<String, String> issuerInitiated() {
|
||||
return planVariant("issuer_initiated");
|
||||
}
|
||||
|
||||
protected static final Map<String, String> 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<String, String> 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<ModuleRun> 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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String, ComponentExportRepresentation> 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<String, ComponentExportRepresentation> 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<ProtocolMapperRepresentation> protocolMappers(String scopeName) {
|
||||
private static List<ProtocolMapperRepresentation> 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<ProtocolMapperRepresentation> mdocProtocolMappers() {
|
||||
private static List<ProtocolMapperRepresentation> 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<String, String> config) {
|
||||
private static ProtocolMapperRepresentation mapper(String name, String type, Map<String, String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<BrowserFlow> 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) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> 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) {
|
||||
@@ -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<String> 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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(HAIP_PLAN, WALLET_INITIATED, MODULES,
|
||||
return discoverModuleVariants(HAIP_PLAN, walletInitiated(), MODULES,
|
||||
ConformanceResult.PASSED, BrowserInteraction.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(HAIP_PLAN, WALLET_INITIATED, MODULES,
|
||||
return discoverModuleVariants(HAIP_PLAN, walletInitiated(), MODULES,
|
||||
ConformanceResult.PASSED, BrowserInteraction.NONE);
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(HAIP_PLAN, WALLET_INITIATED, MODULES,
|
||||
return discoverModuleVariants(HAIP_PLAN, walletInitiated(), MODULES,
|
||||
ConformanceResult.PASSED, BrowserInteraction.ERROR_CALLBACK);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> 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
|
||||
@@ -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<ConformanceModuleVariant> 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);
|
||||
@@ -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<ConformanceModuleVariant> 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."));
|
||||
@@ -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<ConformanceModuleVariant> 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"));
|
||||
@@ -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<ConformanceModuleVariant> 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."));
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"fapi2-security-profile-final-ensure-holder-of-key-required",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"fapi2-security-profile-final-refresh-token",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"fapi2-security-profile-final-user-rejects-authentication",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.DENY_CONSENT);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-on-access-token-in-query",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> 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) {
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-client-attestation-exp-in-past",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.NONE);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-client-attestation-no-sub",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.NONE);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-client-attestation-pop-wrong-aud",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.NONE);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-happy-flow-additional-requests",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-happy-flow-multiple-clients",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-happy-flow-skip-notification",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-happy-flow",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
ISSUER_INITIATED,
|
||||
issuerInitiated(),
|
||||
"oid4vci-1_0-issuer-batch-issuance",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
ISSUER_INITIATED,
|
||||
issuerInitiated(),
|
||||
"oid4vci-1_0-issuer-happy-flow",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN)
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-invalid-client-attestation-pop-signature",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.NONE);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-invalid-client-attestation-signature",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.NONE);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-invalid-jwt-proof-signature",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-invalid-key-attestation-signature",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-invalid-nonce",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> 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) {
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-mismatched-client-attestation-pop-key",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.NONE);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-missing-proof",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-metadata-test-signed",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.NONE);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-unknown-credential-configuration",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-unknown-credential-identifier",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-metadata-test",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.NONE);
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
HAIP_PLAN,
|
||||
WALLET_INITIATED,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-unsupported-encryption-algorithm",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
@@ -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<ConformanceModuleVariant> 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,
|
||||
@@ -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<String, String> 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<String, String> encrypted(Map<String, String> planVariant) {
|
||||
Map<String, String> 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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-on-access-token-in-query",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-happy-flow-additional-requests",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-happy-flow-multiple-clients",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-happy-flow-skip-notification",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-happy-flow",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
issuerInitiated(),
|
||||
"oid4vci-1_0-issuer-batch-issuance",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> 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")));
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-invalid-jwt-proof-signature",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> 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();
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-invalid-nonce",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> 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")));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-missing-proof",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-metadata-test-signed",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.NONE);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-unknown-credential-configuration",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-fail-unknown-credential-identifier",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.LOGIN);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
NON_HAIP_PLAN,
|
||||
walletInitiated(),
|
||||
"oid4vci-1_0-issuer-metadata-test",
|
||||
ConformanceResult.PASSED,
|
||||
BrowserInteraction.NONE);
|
||||
}
|
||||
}
|
||||
@@ -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<ConformanceModuleVariant> 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);
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ abstract class AbstractVpConformanceTest extends AbstractConformanceTest {
|
||||
protected Consumer<ModuleRun> 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()))) {
|
||||
|
||||
@@ -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() : ""));
|
||||
}
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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<ConformanceModuleVariant> moduleVariants() {
|
||||
return discoverModuleVariants(
|
||||
"oid4vp-1final-verifier-test-plan",
|
||||
TEST_PLAN,
|
||||
Map.of(
|
||||
"vp_profile", "plain_vp",
|
||||
"credential_format", "sd_jwt_vc",
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user