Skip to main content
Version: v0.25.0 (Latest)

DID-Based Trust

Decentralized Identifiers (DIDs) are self-sovereign identifiers that resolve to DID Documents containing public keys and service endpoints. While DIDs are self-issued by design, real-world applications still need to answer the question: "Should I trust this DID?"

The IDK provides a DID trust validation layer that applies configurable policies to determine whether a given DID should be trusted. This is separate from DID resolution itself; the trust layer decides policy, while the DID resolver handles resolution.

How It Works

The DidTrustValidationService implements the TrustValidationService interface and is automatically contributed to the session scope via @ContributesIntoSet(SessionScope::class). It handles the TrustContext.TYPE_DID trust type.

Validation follows a two-layer approach:

Layer 1: Method Allow-List Gate

Before any other checks, the service verifies that the DID method (the second segment of a DID, such as web in did:web:example.com) is in the configured list of allowed methods. If the method is not allowed, validation fails immediately. This provides a coarse-grained first filter. For example, you might allow web and key methods but reject peer DIDs.

Layer 2: Trust List Check

If the DID method passes the allow-list gate, the service checks whether the specific DID appears in the configured trusted DIDs list. This is a fine-grained check that lets you enumerate exactly which DIDs your application trusts. When no trusted DIDs list is configured, any DID with an allowed method passes this layer.

Layer 3: Controller Chain Validation

Finally, the service validates the DID controller chain. A DID Document may declare a controller field pointing to another DID. The service verifies that the controller chain is valid and terminates at a trusted DID or at the DID itself (self-controlled).

Usage

Access the trust validation service from the session graph and call validate() with a TrustContext of type TYPE_DID:

val trustValidationService = session.graph.trustValidationService

// Build a trust context for DID trust
val context = TrustContext(
type = TrustContext.TYPE_DID,
parameters = mapOf(
"did" to "did:web:issuer.example.com"
)
)

val result = trustValidationService.validate(context)

when (result.status) {
TrustStatus.TRUSTED -> {
println("DID is trusted")
println("Trust anchor: ${result.trustAnchor?.name}")
}
TrustStatus.UNTRUSTED -> {
println("DID is not trusted: ${result.details}")
}
TrustStatus.VALIDATION_ERROR -> {
println("Validation error: ${result.details}")
}
else -> {
println("Status: ${result.status}, details: ${result.details}")
}
}

Overriding Allowed Methods and Trusted DIDs

You can override the configured allow-lists on a per-request basis:

val context = TrustContext(
type = TrustContext.TYPE_DID,
parameters = mapOf(
"did" to "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"allowedMethods" to "key,web",
"trustedDids" to "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK,did:web:trusted.example.com"
)
)

val result = trustValidationService.validate(context)

Context Parameters

ParameterRequiredDescription
didYesThe DID to validate
allowedMethodsNoComma-separated list of allowed DID methods; overrides config
trustedDidsNoComma-separated list of explicitly trusted DIDs; overrides config
identifierJsonNoPre-resolved DID Document as a JSON string (avoids re-resolution)

Using the Command Directly

You can also invoke the validation command directly via the command ID trust.did.validate:

val commandManager = session.graph.commandManager

val result = commandManager.execute(
ValidateDidTrustCommand(
did = "did:web:issuer.example.com",
allowedMethods = listOf("web", "key", "jwk"),
trustedDids = listOf(
"did:web:issuer.example.com",
"did:web:another-trusted.example.com"
)
)
)

Configuration

Enable and configure DID trust validation via properties:

# Enable DID trust validation
trust.anchors.did.enabled=false

# Explicitly trusted DIDs (comma-separated)
trust.anchors.did.trusted-dids=did:web:example.com,did:key:z6Mk...

# Allowed DID methods (comma-separated, without the "did:" prefix)
trust.anchors.did.allowed-methods=web,key,jwk

When trust.anchors.did.enabled is set to true but no trusted-dids are configured, the service will trust any DID whose method appears in allowed-methods. When trusted-dids is configured, only DIDs that match both the method allow-list and the trusted DIDs list are considered trusted.

Module Dependencies

The DID trust module requires lib-trust-did and lib-did-resolver on the classpath. Add the following to your build.gradle.kts:

dependencies {
implementation("com.sphereon.idk:lib-trust-did:<version>")
implementation("com.sphereon.idk:lib-did-resolver:<version>")
}

The DidTrustValidationService is automatically discovered and registered when these modules are present on the classpath. No additional wiring is needed beyond enabling the feature in configuration.