Java SDK — M2M Authentication Against the Verifier¶
EUDIStack publishes a Java SDK so your backend service (with no human user involved) can authenticate against an OID4VP-compatible Verifier and obtain an OAuth2 access_token, proving possession of a private key bound to a machine credential (e.g. LEARCredentialMachine).
eudistack-verifier-client-java on Maven Central Source code
When to use this guide?
Use this SDK if your integration is:
- Machine-to-machine — a backend service authenticating on its own, with no user present in the flow.
- Against any Verifier compatible with this M2M profile — not tied to a specific EUDIStack implementation.
- In Java, without wanting to depend on Spring or a reactive stack — the SDK brings neither.
If your integration has a human user presenting a credential (login, SSO), see OIDC IdP or SSO across applications instead.
Prerequisites
- An EC P-256 private key — as JWK JSON or as a hex-encoded scalar (
0x...), depending on the format your issuer hands out. - A machine credential (e.g.
LEARCredentialMachine) binding that key as itscnf(confirmation key). - The URL of the Verifier you're authenticating against.
- Java 21 or later.
The protocol, in short¶
The SDK implements private_key_jwt (RFC 7523) with a self-signed Verifiable Presentation as proof of possession — no client pre-registration needed at the Verifier.
sequenceDiagram
autonumber
participant App as Your service (SDK)
participant V as Verifier
Note over App: Builds VerifierM2MClient
App->>App: Derives the public key and compares it<br/>against the credential's cnf (fail-fast)
App->>App: Builds a self-signed VP-JWT<br/>(the credential is embedded)
App->>App: Builds the client_assertion<br/>(RFC 7523) wrapping the VP-JWT
App->>V: POST /oidc/token (client_credentials)
activate V
V->>V: Validates the VP-JWT signature<br/>against its own jwk header
V->>V: Resolves the tenant from<br/>mandate.power[].domain
deactivate V
V-->>App: access_token (Bearer) The private key never leaves your process except embedded in the VP-JWT you sign — the SDK never sends the private key itself anywhere.
Installation¶
Check the artifact page on Maven Central for the latest version.
Basic usage¶
You configure 3 values — your private key, your machine credential, and the Verifier's URL. Tenant and client_id are derived automatically from the credential.
VerifierM2MClient client = VerifierM2MClient.builder()
.verifierUrl("https://verifier.example.org")
.privateKey(privateKey) // JWK JSON or a hex scalar — see below
.credentialJwt(machineCredentialJwt)
.build();
AccessToken token = client.authenticate();
System.out.println(token.accessToken());
build() immediately validates that your private key matches the credential's cnf — a misconfigured key/credential pair fails at startup, not on the first real request.
VerifierM2MClient client = VerifierM2MClient.fromConfig("verifier-client.yaml");
AccessToken token = client.authenticate();
verifier:
url: https://verifier.example.org
client:
private-key-path: key.jwk.json
credential-jwt-path: credential.jwt
.properties is also supported. See the SDK's README for the full key reference.
Accepted private key formats¶
The same parameter accepts either format interchangeably — the SDK detects which one you passed:
With or without the 0x prefix, with or without zero-padding to the full 32 bytes. Some issuers hand out the key this way instead of as a JWK — the SDK derives the corresponding public key automatically, with no extra step on your side.
Error handling¶
| Exception | Thrown when |
|---|---|
InvalidConfigurationException | A required value is missing, the key/credential is invalid, or the config file is malformed |
CredentialKeyMismatchException | The configured private key does not correspond to the credential's cnf |
TokenRequestFailedException | The Verifier rejected the request or was unreachable — exposes httpStatus() and responseBody() |
All extend VerifierClientException.
Troubleshooting
| Symptom | Likely cause | Resolution |
|---|---|---|
CredentialKeyMismatchException at client construction | The private key doesn't correspond to the credential's cnf | Verify you're using the right key/credential pair — the failure is intentionally immediate, not at authenticate() |
InvalidConfigurationException: verifierUrl must use https | You're pointing at a non-TLS Verifier | The SDK requires https by default; use .allowInsecureHttp() only for local testing, never in production |
TokenRequestFailedException with invalid_client | Tenant mismatch | The Verifier derives the tenant from the credential's mandate.power[].domain — it must match the target tenant |
Exception loading .yaml | org.yaml:snakeyaml is missing from the classpath | YAML support is an optional dependency — add it, or use a .properties file |
Security considerations
- The SDK requires
httpsby default — theclient_assertionit sends is a reusable credential for a short window, and sending it in cleartext would let a network observer capture and replay it. - Never enable
.allowInsecureHttp()against a production Verifier. - If you load the private key inline from a
.yamlfile, quote the value (private-key: '0xb8c0...') — an unquoted value that looks numeric may be parsed as a number by the YAML parser instead of as text.
Full reference¶
For the exhaustive configuration reference, further examples, and the protocol details, see the SDK repository's README.