External DID Web Hosting

This guide explains how to make DIDs available at a custom domain (e.g., dids.waltid.org) instead of the default enterprise domain.

Overview

By default, DIDs in the walt.id Enterprise Stack are hosted at the enterprise domain (e.g., waltid.enterprise.cloud). However, you may want to make these DIDs available at your own custom domain (e.g., dids.waltid.org). This guide explains how to set up external DID web hosting to achieve this.

Prerequisites

  • Access to a walt.id Enterprise Stack organization
  • A tenant with DID registry service
  • A custom domain that you control (e.g., dids.waltid.org)
  • Access to configure DNS settings for your custom domain

Step 1: Configure Organization-Level Root DID Registry

Before configuring the tenant-level DID registry, you need to set up an organization-level root DID registry:

  1. Create Organization-Level DID Registry Service:
CURL

Endpoint: /v1/{target}/resource-api/services/create | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise.cloud/v1/{target}/resource-api/services/create' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "type": "did-registry"
}'

Path Parameters

  • orgID: - When performing operations within an organization, it is essential to use the organization's Base URL or another valid host alias. For example, if your organization is named waltid, your default Base URL will be waltid.enterprise.cloud.
  • target: resourceIdentifier - The target indicates the organization in which to create the new organization level DID Registry service, e.g. waltid.tenant.didregistry

Body Parameters

  • type: serviceType - Specifies the type of service to create. In our case did-registry
  1. Set Up Root DID Registry:
CURL

Endpoint: /v1/organization/update-config | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise.cloud/v1/organization/update-config' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "rootDidRegistry": "waltid.tenant.didregistry"
}'

Path Parameters

  • orgID: - When performing operations within an organization, it is essential to use the organization's Base URL or another valid host alias. For example, if your organization is named waltid, your default Base URL will be waltid.enterprise.cloud.

Body Parameters

  • rootDidRegistry: resourceIdentifier - The ID of the DID Registry Service to be used for organization-level DIDs, e.g. waltid.tenant.didregistry

Step 2: Configure Tenant-Level DID Registry

First, ensure that your tenant has the DID registry service properly configured:

CURL

Endpoint:/v1/{target}/tenant-api/tenants/config/update | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise.cloud/v1/{target}/tenant-api/tenants/config/update' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "didRegistry": "waltid.tenant.didregistry"
}'

Body

{
  "didRegistry": "waltid.tenant.didregistry"
}

Path Parameters

  • orgID: - When performing operations within an organization, it is essential to use the organization's Base URL or another valid host alias. For example, if your organization is named waltid, your default Base URL will be waltid.enterprise.cloud.
  • target: resourceIdentifier - The target indicates the tenant for which to update the configuration ({organizationID}.{tenantID}), e.g. waltid.tenant

Body Parameters

  • didRegistry: String - The ID of the DID Registry service to be associated with the tenant. This should be the fully qualified ID of your DID registry service.

Step 3: Create Host Alias for Tenant

Create a host alias for your tenant that points to your custom domain:

CURL

Endpoint:/v1/{target}/resource-api/host-aliases/create | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise.cloud/v1/{target}/resource-api/host-aliases/create' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "domain": "dids.waltid.org"
}'

Body

{
  "domain": "dids.waltid.org"
}

Path Parameters

  • orgID: - When performing operations within an organization, it is essential to use the organization's Base URL or another valid host alias. For example, if your organization is named waltid, your default Base URL will be waltid.enterprise.cloud.
  • target: resourceIdentifier - The target indicates the tenant for which to create the host alias ({organizationID}.{tenantID}.{host_alias_id}), e.g. waltid.tenant.my_alias`

Body Parameters

  • domain: String - The custom domain that you want to use for your tenant. This is the domain where your DIDs will be available.

Step 4: Configure DNS and Reverse Proxy (Customer Side)

On the customer side, you need to configure your DNS and set up a reverse proxy to point your custom domain to the enterprise stack server:

  1. DNS Configuration:
    • Create a DNS record for your custom domain (e.g., dids.waltid.org) pointing to your reverse proxy server.
  2. Reverse Proxy Setup:
    • Set up a reverse proxy (e.g., Nginx, Apache, Traefik) on your server.
    • Configure SSL/TLS for your custom domain.
    • Configure the reverse proxy to forward requests from your custom domain to the enterprise stack server.

Example Nginx Configuration:

server {
    listen 443 ssl;
    server_name dids.waltid.org;

    ssl_certificate /path/to/your/certificate.crt;
    ssl_certificate_key /path/to/your/private.key;

    location / {
        proxy_pass https://waltid.enterprise.cloud;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Example Kubernetes Ingress Configuration:

If you're using Kubernetes, you can set up an Ingress resource to route traffic to your enterprise API service:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: dids-waltid-org-enterprise-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/use-regex: "true"
spec:
  ingressClassName: "nginx"
  tls:
    - hosts:
        - dids.waltid.org
      secretName: did-waltid-org-tls
  rules:
    - host: dids.waltid.org
      http:
        paths:
          - path: /auth/
            pathType: Prefix
            backend:
              service:
                name: waltid-enterprise-api
                port:
                  name: http
          - path: /v1/
            pathType: Prefix
            backend:
              service:
                name: waltid-enterprise-api
                port:
                  name: http
          - path: /.well-known/vct/v1/
            pathType: ImplementationSpecific
            backend:
              service:
                name: waltid-enterprise-api
                port:
                  name: http
          - path: /swagger
            pathType: Prefix
            backend:
              service:
                name: waltid-enterprise-api
                port:
                  name: http
          - path: /api.json
            pathType: ImplementationSpecific
            backend:
              service:
                name: waltid-enterprise-api
                port:
                  name: http
          - path: /.*/did\.json$
            pathType: ImplementationSpecific
            backend:
              service:
                name: waltid-enterprise-api
                port:
                  name: http
          - path: /livez
            pathType: Prefix
            backend:
              service:
                name: waltid-enterprise-api
                port:
                  name: http
          - path: /features/
            pathType: Prefix
            backend:
              service:
                name: waltid-enterprise-api
                port:
                  name: http
          - path: /debug/
            pathType: Prefix
            backend:
              service:
                name: waltid-enterprise-api
                port:
                  name: http

This Ingress configuration:

  • Sets up TLS for the dids.waltid.org domain using cert-manager
  • Routes various API paths to the waltid-enterprise-api service
  • Includes a regex path for DID documents (/.*/did.json$)

Step 5: Create DID in DID Store

Create a DID in the DID Store, making sure to use your custom domain in the domain parameter:

CURL

Endpoint: /v1/{target}/did-service-api/dids/create/web | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise.cloud/v1/{target}/did-service-api/dids/create/web' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "type": "single_key_id",
  "keyId": "waltid.tenant.kms1.secp256r1",
  "domain": "dids.waltid.org",
  "path" : "user1"
}'

Body

{
  "type": "single_key_id",
  "keyId": "waltid.tenant.kms1.secp256r1",
  "domain": "dids.waltid.org",
  "path" : "user1"
}

Path Parameters

  • orgID: - When performing operations within an organization, it is essential to use the organization's Base URL or another valid host alias. For example, if your organization is named waltid, your default Base URL will be waltid.enterprise.cloud.
  • target: resourceIdentifier - The target indicates the organization + tenant + DID service in which to execute the DID creation ({organizationID}.{tenantID}.{subtenantID}.{didServiceID}), e.g. waltid.tenant.did1

Body Parameters

  • keyId: resourceIdentifier - Specifies the key for which to generate a DID + DID Document. Please make sure that the key is stored in a KMS service under the same tenant as the DID service.
  • domain: string - The domain name to be used for the DID:web. This should be your custom domain (e.g., dids.waltid.org).
  • path: string - The path to be used for the DID:web (e.g., user1).

Response

  • 201 - DID created successfully.
{
  "did": "did:web:dids.waltid.org:user1",
  "document": {
    "context": [
      "https://www.w3.org/ns/did/v1",
      "https://w3id.org/security/suites/jws-2020/v1"
    ],
    "id": "did:web:dids.waltid.org:user1",
    "verificationMethod": [
      {
        "id": "did:web:dids.waltid.org:user1#key1",
        "type": "JsonWebKey2020",
        "controller": "did:web:dids.waltid.org:user1",
        "publicKeyJwk": {
          "kty": "EC",
          "crv": "P-256",
          "kid": "key1",
          "x": "...",
          "y": "..."
        }
      }
    ],
    "assertionMethod": [
      "did:web:dids.waltid.org:user1#key1"
    ],
    "authentication": [
      "did:web:dids.waltid.org:user1#key1"
    ],
    "capabilityDelegation": [
      "did:web:dids.waltid.org:user1#key1"
    ],
    "capabilityInvocation": [
      "did:web:dids.waltid.org:user1#key1"
    ],
    "keyAgreement": [
      "did:web:dids.waltid.org:user1#key1"
    ]
  }
}

Step 6: Verify DID Resolution

Once you've completed the above steps, your DID should be available to resolve at your custom domain:

did:web:dids.waltid.org:user1

You can verify this by accessing the DID document directly at:

https://dids.waltid.org/user1/did.json

Troubleshooting

If you're having issues with external DID web hosting, check the following:

  1. Organization-Level DID Registry: Ensure that your organization has the correct root DID registry configured.
  2. Tenant Configuration: Ensure that your tenant has the correct DID registry configured.
  3. Host Alias: Verify that the host alias has been created correctly with your custom domain.
  4. DNS Configuration: Check that your DNS records are pointing to the correct server.
  5. Reverse Proxy: Ensure that your reverse proxy is correctly configured to forward requests to the enterprise stack server.
  6. DID Creation: Make sure you used your custom domain when creating the DID.
Last updated on July 23, 2025