Technical Details
This page lists the specifications UniCore implements and describes how it is built. It is aimed at architects and developers who want to evaluate UniCore or integrate it with other systems, for example to check whether a wallet is compatible.
Specifications
OpenID for Verifiable Credentials
| Specification | Version | Role in UniCore |
|---|---|---|
| OpenID for Verifiable Credential Issuance (OpenID4VCI) | 1.0 | Issuer and holder |
| OpenID for Verifiable Presentations (OpenID4VP) | 1.0 | Verifier |
| Self-Issued OpenID Provider v2 (SIOPv2) | Draft 13 | Verifier |
A wallet that wants to receive credentials from UniCore or present them to it needs to support OpenID4VCI 1.0 and OpenID4VP 1.0. The UniMe wallet is built on the same OpenID4VC library as UniCore.
Issuance (OpenID4VCI)
- Pre-authorized code flow (optionally protected with a transaction code) and authorization code flow
- Credential offers by reference
- Nonce Endpoint and Notification Endpoint
- Proof type
jwtfor key binding - Not supported: batch issuance, deferred issuance, encrypted credential responses
Verification (OpenID4VP and SIOPv2)
- Credential requests are expressed as Digital Credentials Query Language (DCQL) queries. DIF Presentation Exchange is no longer used.
- Request objects are passed by reference (
request_uri) and signed by UniCore, using thedecentralized_identifierclient identifier prefix. - Responses are received with response mode
direct_post. - If an authorization request contains no DCQL query, UniCore sends a SIOPv2 request and receives an ID Token instead.
OAuth 2.0
UniCore includes an OAuth 2.0 authorization server for the OpenID4VCI flows.
| Specification | Notes |
|---|---|
| RFC 8414: Authorization Server Metadata | Served at /.well-known/oauth-authorization-server |
| RFC 9126: Pushed Authorization Requests (PAR) | Also used for interactive authorization |
| RFC 7636: Proof Key for Code Exchange (PKCE) | Required, only with the S256 method |
DPoP is not supported.
Credential Formats and Data Models
| Format identifier | Description |
|---|---|
jwt_vc_json | W3C Verifiable Credential secured as a JWT |
vc+sd-jwt | W3C Verifiable Credential secured with SD-JWT |
dc+sd-jwt | SD-JWT VC, including Type Metadata |
Credentials can follow these data models:
- W3C Verifiable Credentials Data Model 2.0 (default)
- W3C Verifiable Credentials Data Model 1.1
- Open Badges 3.0
- European Learning Model 3.3
Which format is used for which data model is described in Issuance. The formats ldp_vc and mso_mdoc are not supported.
Credential Status
Revocation and suspension use the IETF OAuth Token Status List (draft 10). Status lists are served as application/statuslist+jwt.
Cryptography
- Signing algorithms: EdDSA (Ed25519, preferred by default) and ES256. SD-JWT credentials and Key Binding JWTs are signed with ES256.
- Keys are generated and stored in an IOTA Stronghold vault.
Decentralized Identifiers
| Specification | Notes |
|---|---|
DID methods did:key, did:jwk, did:web, did:iota | See DID Methods |
| DIF Well Known DID Configuration | See Domain Linkage |
| DIF Linked Verifiable Presentations | Published as a service in the DID Document |
API and Integration
| Specification | Notes |
|---|---|
| OpenAPI 3.1 | See the API references |
| RFC 9457: Problem Details for HTTP APIs | Error responses, see Problem Details |
| CloudEvents 1.0 | Format of events streamed with Server-Sent Events at GET /v0/events |
| OpenTelemetry (OTLP) | Traces, logs and metrics, see Monitoring |
Protocol Endpoints
Besides the management API under /v0, UniCore serves the following endpoints, which wallets and other agents call. They are documented in the API reference including protocol endpoints.
| Endpoint | Purpose |
|---|---|
/.well-known/openid-credential-issuer | OpenID4VCI Credential Issuer Metadata |
/.well-known/oauth-authorization-server | OAuth 2.0 Authorization Server Metadata |
/.well-known/did.json, /.well-known/did-configuration.json | did:web DID Document and Domain Linkage |
/openid4vci/credential-offer/{offer_id} | Credential offer by reference |
/openid4vci/credential, /openid4vci/nonce, /openid4vci/notification | OpenID4VCI Credential, Nonce and Notification Endpoints |
/auth/par, /auth/authorize, /auth/consent, /auth/token | OAuth 2.0 authorization server |
/request/{request_id}, /redirect | OpenID4VP request objects and responses |
/ietf-oauth-token-status-list/{path} | Token Status Lists |
/vct/{credential_configuration_id}/{version} | SD-JWT VC Type Metadata |
/linked-verifiable-presentations/{presentation_id} | Linked Verifiable Presentations |
/healthz, /livez, /readyz | Health probes |
Architecture
UniCore is built with Domain-Driven Design (DDD) and Command Query Responsibility Segregation with Event Sourcing (CQRS-ES).
Bounded Contexts
Each bounded context is a separate crate that owns its domain model: aggregates, commands, events and views.
| Bounded context | Responsibility | Aggregates |
|---|---|---|
agent_issuance | Issuing credentials with OpenID4VCI | server_config, offer, credential, nonce, status_list, public_offer |
agent_authorization | OAuth 2.0 authorization server | oauth2_authorization_request, authorization_code, access_token, client |
agent_verification | Requesting and verifying presentations with OpenID4VP and SIOPv2 | authorization_request |
agent_holder | Receiving credentials and creating presentations as a holder | received_offer, holder_credential, presentation |
agent_identity | DIDs, DID Document services, profile and connections | document, service, profile, connection |
agent_library | Credential templates and catalogs | template, catalog |
The shared terminology of these contexts is defined in the Ubiquitous Language.
CQRS and Event Sourcing
- A request to the API is translated into a command for an aggregate.
- The aggregate validates the command against its current state and either rejects it or emits one or more events, for example
CredentialOfferCreated. - The events are appended to the event store. They are never changed or deleted, and the state of an aggregate is rebuilt by replaying its events.
- Queries update read-optimized views from the events, which the API uses to answer read requests.
- The events are also passed to an event bus, which streams them to clients with Server-Sent Events and to the configured event publishers (see React to Events).
This gives UniCore a complete audit trail of every state change and lets external systems react to it without polling.
The CQRS-ES implementation is based on the cqrs-es crate. These event stores are supported:
| Event store | Notes |
|---|---|
| PostgreSQL | Persistent |
| MongoDB | Persistent, requires a replica set because its change streams feed the event bus |
| In-memory | For development and testing, all data is lost on restart |
Crates
| Crate | Responsibility |
|---|---|
agent_application | Executable: loads the configuration and wires all components together |
agent_api_http | HTTP API, OpenAPI specification and Problem Details |
agent_issuance, agent_authorization, agent_verification, agent_holder, agent_identity, agent_library | Bounded contexts (see above) |
agent_secret_manager | Key management and signing with Stronghold, DID resolution |
agent_store | Event store and view repositories for PostgreSQL, MongoDB and in-memory |
agent_event_publisher_http, agent_event_publisher_nats | Publishing events to external systems via HTTP or NATS |
agent_shared | Configuration, profiles and shared utilities |
shared-kernel | Building blocks for application services, command handlers and the event bus |
Design decisions are recorded as Architecture Decision Records.
Technology Stack
- Written in Rust, using Tokio and axum
- Distributed as the Docker image
impiercetechnologies/ssi-agent - Configured with a
config.yamlfile andUNICORE__*environment variables, see Configuration - Open source under the Apache 2.0 license