taler-swiyu-verifier-manual.rst (41530B)
1 .. 2 This file is part of GNU TALER. 3 4 Copyright (C) 2024, 2025 Taler Systems SA 5 6 TALER is free software; you can redistribute it and/or modify it under the 7 terms of the GNU Affero General Public License as published by the Free Software 8 Foundation; either version 2.1, or (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 11 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 13 14 You should have received a copy of the GNU Affero General Public License along with 15 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 16 17 18 Swiyu Verifier Operator Manual 19 ############################## 20 21 Introduction 22 ============ 23 24 About Swiyu Verifier 25 -------------------- 26 27 The Swiyu Generic Verifier Service is a web server implementing the technical 28 standards specified in the Swiyu Trust Infrastructure Interoperability Profile. 29 It provides the capability to verify Verifiable Credentials held in users' 30 digital wallets using the OpenID for Verifiable Presentations (OID4VP) 31 protocol. 32 33 The verifier service exposes two distinct API interfaces: 34 35 - **Management API**: Internal interface for business systems to create 36 verification requests and retrieve results. This API should only be 37 accessible from within your organization's network. 38 39 - **OID4VP API**: Public interface that implements the OpenID for Verifiable 40 Presentations protocol. This interface must be accessible by wallet 41 applications and requires HTTPS with a valid TLS certificate. 42 43 When integrated with KyCH OAuth2 Gateway, the Swiyu Verifier enables 44 KYC (Know Your Customer) verification flows for GNU Taler exchanges, 45 allowing users to prove their identity attributes using Swiss Beta ID 46 credentials stored in the Swiyu Wallet. 47 48 49 About this manual 50 ----------------- 51 52 This manual targets system administrators who want to install, configure, 53 and operate a Swiyu Verifier service. It covers the onboarding process 54 to the Swiyu Trust Infrastructure, service configuration, integration 55 with KyCH OAuth2 Gateway, and API usage. 56 57 58 Architecture overview 59 --------------------- 60 61 The following diagram illustrates the high-level architecture of credential 62 verification using the Swiyu Verifier: 63 64 :: 65 66 +------------------+ +------------------+ +------------------+ 67 | | | | | | 68 | Business |---->| Swiyu |<----| Swiyu | 69 | Verifier | | Verifier | | Wallet | 70 | (e.g., KyCH) | | Service | | | 71 | | | | | | 72 +------------------+ +------------------+ +------------------+ 73 | 74 v 75 +------------------+ 76 | | 77 | Swiyu | 78 | Registries | 79 | | 80 +------------------+ 81 82 The verification flow proceeds as follows: 83 84 1. The business verifier (such as KyCH) creates a verification request 85 via the Management API, specifying which credential claims to verify. 86 87 2. The verifier service stores the request and returns a verification URI 88 and optional deeplink for QR code generation. 89 90 3. The user's wallet retrieves the verification request object via the 91 OID4VP API and displays the requested claims for user consent. 92 93 4. Upon user consent, the wallet sends the authorization response containing 94 the Verifiable Presentation with the requested credential data. 95 96 5. The verifier service validates the credential by checking the issuer's 97 public key against the Base Registry and verifying the credential status 98 against the Status Registry. 99 100 6. The business verifier retrieves the verification result and disclosed 101 claims either by polling or via webhook notification. 102 103 104 Prerequisites 105 ============= 106 107 System requirements 108 ------------------- 109 110 Before deploying the Swiyu Verifier, ensure your system meets these requirements: 111 112 - **Java Runtime Environment (JRE)**: Version 25 or higher. The verifier 113 moved to Java 25 with its 5.0.0 release; releases up to 4.2.0 need Java 21. 114 - **PostgreSQL**: Version 12 or higher 115 - **Disk space**: Approximately 100 MB 116 - **Network access**: Outbound access to Swiyu registries and inbound access 117 for wallet connections 118 - **Operating system**: Linux x64/AArch64, macOS AArch64, or Windows x64 119 120 121 HTTPS requirement 122 ----------------- 123 124 The Swiyu Wallet only accepts HTTPS connections with valid TLS certificates. 125 Your verifier's OID4VP endpoints must be accessible via HTTPS using a 126 certificate from a trusted Certificate Authority. 127 128 For development and testing, you can use a tunneling service like ngrok 129 to expose your local verifier with a valid HTTPS URL: 130 131 .. code-block:: shell-session 132 133 $ ngrok http 8080 134 135 This provides a public HTTPS URL (e.g., ``https://abc123.ngrok-free.app``) 136 that forwards to your local verifier. Use this URL as the ``external-url`` 137 in your configuration. 138 139 For production deployments, obtain a proper TLS certificate and configure 140 your reverse proxy or load balancer accordingly. 141 142 143 Onboarding to Swiyu Trust Infrastructure 144 ======================================== 145 146 Before operating a verifier in the Swiyu ecosystem, you must complete the 147 onboarding process to register your service on the Base Registry. This 148 section summarizes the required steps. 149 150 The onboarding process involves several administrative and technical steps 151 that establish your identity as a trusted verifier within the Swiyu Trust 152 Infrastructure. 153 154 .. note:: 155 156 The official Swiyu documentation provides detailed cookbooks that guide 157 you through each step of the onboarding process: 158 159 - `Onboarding the swiyu Base & Trust Registry <https://swiyu-admin-ch.github.io/cookbooks/onboarding-base-and-trust-registry/>`_: 160 Complete guide for registering your organization and managing DIDs 161 162 - `Getting started with the swiyu Generic Verifier <https://swiyu-admin-ch.github.io/cookbooks/onboarding-generic-verifier/>`_: 163 Step-by-step deployment guide for the verifier service 164 165 - `Designing the visualization for Issuer, Verifier and Credential <https://swiyu-admin-ch.github.io/cookbooks/vc-visual-presentation/>`_: 166 How to configure the display metadata shown in wallets 167 168 - `How to use Beta-ID for my business <https://swiyu-admin-ch.github.io/cookbooks/how-to-use-beta-id/>`_: 169 Details about the Swiss Beta ID credential and available claims 170 171 The following sections provide a summary. Refer to the official cookbooks 172 for the most up-to-date and detailed instructions. 173 174 175 Step 1: Access the Swiss Confederacy ePortal 176 -------------------------------------------- 177 178 Sign in or create an account at the Swiss Confederacy ePortal using 179 an AGOV or CH-Login account. This account is required for all 180 subsequent registration steps. 181 182 The ePortal is available at https://www.eportal.admin.ch/. 183 184 185 Step 2: Register as Business Partner 186 ------------------------------------ 187 188 Complete the business partner registration process through the ePortal. 189 This establishes your organization's identity within the trust infrastructure. 190 191 Navigate to the Swiyu Public Beta section and follow the business partner 192 registration workflow. 193 194 195 Step 3: Obtain API Keys 196 ----------------------- 197 198 Access the API self-service portal to generate the API keys required for 199 interacting with the Swiyu registries. These keys authenticate your 200 verifier service when making registry requests. 201 202 The self-service portal provides: 203 204 - API credentials for the Base Registry (DID management) 205 - API credentials for the Status Registry (credential revocation checks) 206 207 208 Step 4: Allocate DID Space 209 -------------------------- 210 211 Request allocation of a Decentralized Identifier (DID) space on the 212 Swiyu Base Registry. This provides a unique namespace for your verifier's 213 cryptographic identity. 214 215 The DID follows the ``did:tdw`` method (Trust DID Web) with the format:: 216 217 did:tdw:<hash>:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:<uuid> 218 219 220 Step 5: Generate Cryptographic Keys 221 ----------------------------------- 222 223 Use the Swiyu DID Toolbox to generate the required cryptographic keys 224 and DID log. The toolbox creates: 225 226 - An EC P-256 private key for signing verification requests 227 - The corresponding DID Document entries 228 - A DID log file for registration 229 230 Download the DID Toolbox from the official Swiyu repository and run: 231 232 .. code-block:: shell-session 233 234 $ java -jar didtoolbox.jar generate 235 236 This creates a ``.didtoolbox`` directory containing your keys and DID log. 237 The key files include: 238 239 - ``auth-key-01``: The private authentication key (keep this secure) 240 - ``did-log.json``: The DID log to upload to the registry 241 242 The generated DID log contains the values needed for verifier configuration: 243 244 - ``value.id``: Your verifier's DID (used for ``client_id``) 245 - ``value.assertionMethod``: The key reference (used for ``signing-key-verification-method``) 246 247 248 Step 6: Upload DID Log 249 ---------------------- 250 251 Upload the generated DID log to the Base Registry using the API keys 252 obtained in Step 3. This publishes your verifier's DID Document, 253 making your public key available for credential verification. 254 255 Use the Base Registry API to upload your DID log:: 256 257 POST https://identifier-reg.trust-infra.swiyu.admin.ch/api/v1/did/<your-uuid>/log 258 259 After successful upload, your DID is resolvable and wallets can verify 260 your signed request objects. 261 262 263 Step 7: (Optional) Become a Trusted Participant 264 ----------------------------------------------- 265 266 Optionally register on the Trust Registry to become a trusted participant. 267 This enables other parties to verify your verifier's trustworthiness 268 through the trust infrastructure. 269 270 See the `Onboarding the swiyu Base & Trust Registry cookbook 271 <https://swiyu-admin-ch.github.io/cookbooks/onboarding-base-and-trust-registry/>`_ 272 for details on trust registry registration. 273 274 275 Installation 276 ============ 277 278 From the Debian package 279 ----------------------- 280 281 On Debian, the verifier is packaged as ``swiyu-verifier``, built from the 282 KyCH source package (which pins an upstream release in a submodule). It 283 installs the service, a systemd unit, a configuration under 284 ``/etc/swiyu-verifier`` and the ``swiyu-verifier-dbconfig`` tool: 285 286 .. code-block:: shell-session 287 288 $ sudo apt install ./swiyu-verifier_*.deb 289 $ sudo swiyu-verifier-dbconfig 290 291 The service ships disabled, because it cannot verify anything before it has 292 a DID and a signing key of its own. See 293 ``/usr/share/doc/swiyu-verifier/README.Debian`` and swiyu-verifier(1) for the 294 remaining steps, and the `Configuration`_ section below for what the settings 295 mean. The rest of this manual describes the service itself, and applies 296 either way. 297 298 299 Building from source 300 -------------------- 301 302 Clone the Swiyu Verifier repository and build using Maven: 303 304 .. code-block:: shell-session 305 306 $ git clone https://github.com/swiyu-admin-ch/swiyu-verifier.git 307 $ cd swiyu-verifier 308 $ ./mvnw clean install -DskipTests 309 310 This produces the executable JAR files in the ``verifier-application/target`` 311 directory. A JDK 25 is required; with an older default JDK on the host, point 312 ``JAVA_HOME`` at it explicitly. 313 314 315 Database setup 316 -------------- 317 318 The Swiyu Verifier requires a PostgreSQL database to store verification 319 requests and results. Create the database and user with appropriate 320 privileges: 321 322 .. code-block:: shell-session 323 324 $ psql -U postgres 325 326 .. code-block:: sql 327 328 CREATE USER verifier_user WITH PASSWORD 'your_secure_password'; 329 CREATE DATABASE verifier_db OWNER verifier_user; 330 GRANT ALL PRIVILEGES ON DATABASE verifier_db TO verifier_user; 331 332 The ``CREATE USER`` command creates a PostgreSQL role that the verifier 333 service will use to authenticate with the database. The ``CREATE DATABASE`` 334 command creates the database and assigns ownership to the verifier user. 335 The ``GRANT`` command ensures the user has full access to the database. 336 337 The verifier creates and migrates its own schema with Flyway on every start, 338 so an empty database owned by its role is all it needs -- including after an 339 upgrade, where there is nothing to do by hand. 340 341 .. warning:: 342 343 Do not set ``spring.jpa.hibernate.ddl-auto`` to ``create`` or 344 ``create-drop`` to achieve this. Those drop the schema, and with it every 345 verification in the database, on each start. The correct value is 346 ``validate``, which has Hibernate check the schema Flyway produced; it is 347 the upstream default. 348 349 350 Configuration 351 ============= 352 353 The Swiyu Verifier uses Spring Boot's externalized configuration system, 354 which supports YAML configuration files, environment variables, and 355 profile-based overrides. This section explains the configuration profiles 356 and how to use them for different deployment scenarios. 357 358 359 Configuration profiles 360 ---------------------- 361 362 Spring Boot profiles allow you to define environment-specific configurations 363 that override the base ``application.yml`` settings. The verifier ships with 364 several pre-configured profiles for different deployment scenarios. 365 366 **Base configuration (application.yml)** 367 368 The base configuration file defines all available settings with environment 369 variable placeholders. This configuration is designed for production 370 deployments where settings are injected via environment variables or 371 container orchestration: 372 373 .. code-block:: yaml 374 375 application: 376 external-url: "${EXTERNAL_URL:}" 377 client_id: "${VERIFIER_DID:}" 378 client_id_prefix: "decentralized_identifier" 379 signing_key: "${secret.signing_key:${SIGNING_KEY:}}" 380 signing-key-verification-method: "${DID_VERIFICATION_METHOD:}" 381 client-metadata-file: "${OPENID_CLIENT_METADATA_FILE:}" 382 deeplink-schema: ${DEEPLINK_SCHEMA:swiyu-verify} 383 additional-audit-information: 384 credential-subject-data-enabled: ${ADDITIONAL_AUDIT_INFORMATION_CREDENTIAL_SUBJECT_DATA_ENABLED:true} 385 386 spring: 387 datasource: 388 url: "${POSTGRES_JDBC}" 389 username: "${secret.db.username:${POSTGRES_USER}}" 390 password: "${secret.db.password:${POSTGRES_PASSWORD}}" 391 flyway: 392 enabled: true 393 jpa: 394 hibernate: 395 ddl-auto: validate 396 397 The ``${VARIABLE:default}`` syntax means the value is read from the 398 environment variable ``VARIABLE``, falling back to ``default`` if not set. 399 The nested form ``${secret.value:${ENV_VAR:}}`` first checks for a 400 mounted secret file, then an environment variable. 401 402 **Local profile (application-local.yml)** 403 404 The ``local`` profile is for development with Docker Compose managing the 405 PostgreSQL database. When it is active, Spring Boot starts a PostgreSQL 406 container from ``compose.yaml``, and the profile supplies a throwaway signing 407 key and example DID values -- which are of course not valid for real 408 verifications: 409 410 .. code-block:: yaml 411 412 application: 413 signing_key: "-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----\n" 414 signing-key-verification-method: "did:example:12345#key-1" 415 external-url: "http://${server.host}:${server.port}" 416 client-metadata-file: "classpath:client_metadata.json" 417 418 spring: 419 docker: 420 compose: 421 enabled: true 422 file: compose.yaml 423 datasource: 424 url: "jdbc:postgresql://localhost:5434/verifier_db" 425 username: "verifier_user" 426 password: "secret" 427 jpa: 428 hibernate: 429 ddl-auto: validate 430 431 .. note:: 432 433 Earlier releases also shipped a ``local-dockerless`` profile. It is gone; 434 for development against a PostgreSQL of your own, supply a configuration 435 file instead of activating a profile, as the next section describes. 436 437 438 **A configuration file of your own** 439 440 Rather than editing the upstream tree or activating a profile, pass an extra 441 configuration file, which Spring Boot reads after the one inside the jar and 442 which therefore overrides it: 443 444 .. code-block:: shell-session 445 446 $ ./mvnw spring-boot:run -pl verifier-application \ 447 -Dspring-boot.run.jvmArguments=\ 448 "-Dspring.config.additional-location=file:/path/to/my-verifier.yml" 449 450 with ``my-verifier.yml``: 451 452 .. code-block:: yaml 453 454 application: 455 external-url: "https://your-ngrok-url.ngrok-free.app" 456 client_id: "did:tdw:QmHash:identifier-reg.trust-infra.swiyu-int.admin.ch:api:v1:did:your-uuid" 457 signing-key-verification-method: "did:tdw:QmHash:identifier-reg.trust-infra.swiyu-int.admin.ch:api:v1:did:your-uuid#auth-key-01" 458 signing_key: | 459 -----BEGIN EC PRIVATE KEY----- 460 MHcCAQEEI...your_actual_key... 461 -----END EC PRIVATE KEY----- 462 client-metadata-file: "file:./client_metadata.json" 463 464 spring: 465 docker: 466 compose: 467 enabled: false 468 datasource: 469 url: "jdbc:postgresql://localhost:5432/verifier_db" 470 username: "verifier_user" 471 password: "secret" 472 jpa: 473 hibernate: 474 ddl-auto: validate 475 476 webhook: 477 callback-uri: "https://kych.example.com/notification" 478 callback-interval: 5000 479 480 This is what ``swiyu-verifier/run.sh`` in the KyCH tree does, and what the 481 Debian package does with ``/etc/swiyu-verifier/application.yml``. 482 483 Note that ``client_id_scheme`` is no longer a setting: release 4.0.0 replaced 484 it with ``client_id_prefix``, which defaults to ``decentralized_identifier`` 485 and is prepended to ``client_id`` in what the wallet receives. 486 487 488 Environment variables 489 --------------------- 490 491 Every setting of the base configuration has an environment variable, which is 492 how a container deployment and the Debian package both configure the service: 493 494 .. code-block:: bash 495 496 # Verifier identity 497 EXTERNAL_URL="https://your-verifier.example.com" 498 VERIFIER_DID="did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:your-uuid" 499 DID_VERIFICATION_METHOD="did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:your-uuid#auth-key-01" 500 OPENID_CLIENT_METADATA_FILE="file:/etc/swiyu-verifier/client_metadata.json" 501 502 # Database connection 503 POSTGRES_USER="swiyu_verifier" 504 POSTGRES_PASSWORD="..." 505 POSTGRES_JDBC="jdbc:postgresql://localhost:5432/swiyu_verifier" 506 507 The signing key is the exception worth knowing about. It is a multi-line PEM 508 document, so it cannot be carried in a systemd environment file, and an 509 escaped ``\n`` in such a file reaches the JVM as two characters, which fails 510 with ``No PEM key found``. Put it in a YAML file -- where a block scalar 511 expresses it directly -- and have the service import it; the Debian package 512 ships ``/usr/share/swiyu-verifier/signing-key.yml.example`` for exactly this 513 and imports ``/etc/swiyu-verifier/secrets/signing-key.yml`` with 514 ``spring.config.import``. 515 516 517 Deployment Options 518 ================== 519 520 The Swiyu Verifier supports multiple deployment strategies depending on 521 your infrastructure and requirements. 522 523 524 Local development without Docker 525 -------------------------------- 526 527 This approach runs the verifier directly on your machine with a locally 528 installed PostgreSQL database. 529 530 **1. Set up PostgreSQL** 531 532 Create the database and user: 533 534 .. code-block:: shell-session 535 536 $ psql -d postgres -c "CREATE USER verifier_user WITH PASSWORD 'secret';" 537 $ psql -d postgres -c "CREATE DATABASE verifier_db OWNER verifier_user;" 538 $ psql -d verifier_db -c "GRANT ALL PRIVILEGES ON DATABASE verifier_db TO verifier_user;" 539 540 **2. Write a configuration file** 541 542 Put your DID, verification method and signing key from the Swiyu onboarding 543 into a YAML file of your own, as "A configuration file of your own" above 544 describes. (There is no ``local-dockerless`` profile any more.) 545 546 **3. Start the verifier** 547 548 .. code-block:: shell-session 549 550 $ ./mvnw spring-boot:run -pl verifier-application \ 551 -Dspring-boot.run.jvmArguments=\ 552 "-Dspring.config.additional-location=file:/path/to/my-verifier.yml" 553 554 **4. Expose via ngrok (for wallet testing)** 555 556 Since wallets require HTTPS, use ngrok to expose your local verifier: 557 558 .. code-block:: shell-session 559 560 $ ngrok http 8080 561 562 Update the ``external-url`` in your configuration with the ngrok URL. 563 564 565 Local development with Docker Compose 566 ------------------------------------- 567 568 This approach uses Docker Compose to manage the PostgreSQL database while 569 running the verifier application directly. 570 571 **1. Start the verifier with Docker Compose support** 572 573 .. code-block:: shell-session 574 575 $ ./mvnw spring-boot:run -pl verifier-application \ 576 -Dspring-boot.run.profiles=local 577 578 Spring Boot automatically starts the PostgreSQL container defined in 579 ``compose.yaml`` before the application starts. 580 581 **2. Verify the database container** 582 583 .. code-block:: shell-session 584 585 $ docker ps 586 CONTAINER ID IMAGE PORTS NAMES 587 abc123def456 postgres:15-alpine 0.0.0.0:5434->5432/tcp verifier_postgres 588 589 The database is accessible on port 5434 to avoid conflicts with any 590 existing PostgreSQL installation. 591 592 593 Docker Compose full deployment 594 ------------------------------ 595 596 For testing the complete stack in containers, use the sample compose file 597 that includes both the verifier service and database: 598 599 **1. Create a .env file with your credentials** 600 601 .. code-block:: bash 602 603 EXTERNAL_URL=https://your-verifier.example.com 604 VERIFIER_DID=did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:your-uuid 605 DID_VERIFICATION_METHOD=did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:your-uuid#auth-key-01 606 SIGNING_KEY="-----BEGIN EC PRIVATE KEY----- 607 ...your key... 608 -----END EC PRIVATE KEY-----" 609 OPENID_CLIENT_METADATA_FILE=/verifier_metadata.json 610 611 **2. Start the stack** 612 613 .. code-block:: shell-session 614 615 $ docker compose -f sample.compose.yml up -d 616 617 This starts: 618 619 - ``verifier_postgres``: PostgreSQL 15 database on internal port 5432 620 - ``verifier-service``: The verifier application on port 8083 621 622 **3. Verify the deployment** 623 624 .. code-block:: shell-session 625 626 $ curl http://localhost:8083/swagger-ui/index.html 627 628 The sample compose file: 629 630 .. code-block:: yaml 631 632 services: 633 verifier_postgres: 634 image: postgres:15-alpine 635 environment: 636 POSTGRES_USER: "verifier_user" 637 POSTGRES_PASSWORD: "secret" 638 POSTGRES_DB: "verifier_db" 639 ports: 640 - "5434:5432" 641 healthcheck: 642 test: ["CMD-SHELL", "pg_isready -U verifier_user -d verifier_db"] 643 interval: 5s 644 timeout: 5s 645 retries: 5 646 647 verifier-service: 648 image: ghcr.io/swiyu-admin-ch/swiyu-verifier:latest 649 configs: 650 - source: verifier_metadata 651 target: /verifier_metadata.json 652 ports: 653 - "8083:8080" 654 environment: 655 EXTERNAL_URL: ${EXTERNAL_URL} 656 OPENID_CLIENT_METADATA_FILE: ${OPENID_CLIENT_METADATA_FILE} 657 VERIFIER_DID: ${VERIFIER_DID} 658 DID_VERIFICATION_METHOD: ${DID_VERIFICATION_METHOD} 659 SIGNING_KEY: ${SIGNING_KEY} 660 POSTGRES_USER: "verifier_user" 661 POSTGRES_PASSWORD: "secret" 662 POSTGRES_JDBC: "jdbc:postgresql://verifier_postgres:5432/verifier_db" 663 664 configs: 665 verifier_metadata: 666 content: | 667 { 668 "client_id": "${VERIFIER_DID}", 669 "client_name": "My Verifier", 670 ... 671 } 672 673 674 Debug mode 675 ---------- 676 677 To run the verifier with remote debugging enabled: 678 679 .. code-block:: shell-session 680 681 $ ./mvnw spring-boot:run -pl verifier-application \ 682 -Dspring-boot.run.fork=true \ 683 -Dspring-boot.run.jvmArguments="-Dspring.config.additional-location=file:/path/to/my-verifier.yml -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" 684 685 This starts the JVM debug agent on port 5005. Connect your IDE's remote 686 debugger to ``localhost:5005``. 687 688 689 Identity configuration 690 ---------------------- 691 692 These settings establish your verifier's identity within the Swiyu 693 Trust Infrastructure. All values are derived from the DID generation 694 process during onboarding. 695 696 ``application.client_id`` 697 Your verifier's DID as registered on the Base Registry. This uniquely 698 identifies your service to wallets and other participants. 699 Example: ``did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:uuid`` 700 701 ``application.client_id_prefix`` 702 Prefix put in front of ``client_id`` in what the wallet receives, as in 703 ``decentralized_identifier:did:tdw:...``. Defaults to 704 ``decentralized_identifier`` and rarely needs changing. It replaced the 705 ``client_id_scheme`` setting in release 4.0.0. 706 707 ``application.signing-key-verification-method`` 708 The full DID with key fragment that identifies the public key in your 709 DID Document. This tells wallets where to find the key for verifying 710 your signed request objects. 711 Example: ``did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:uuid#auth-key-01`` 712 713 ``application.signing_key`` 714 The private EC key used to sign OID4VP request objects. This must be 715 the private key corresponding to the public key published in your DID 716 Document. Include the full PEM content including BEGIN and END markers:: 717 718 application: 719 signing_key: | 720 -----BEGIN EC PRIVATE KEY----- 721 MHcCAQEEI...your_key_content... 722 -----END EC PRIVATE KEY----- 723 724 ``application.external-url`` 725 The publicly accessible URL of your verifier service. Wallets use this 726 URL to retrieve request objects and submit presentations. Must be HTTPS 727 in production. 728 Example: ``https://verifier.example.com`` 729 730 ``application.client-metadata-file`` 731 Path to the verifier metadata JSON file. This file contains display 732 information shown to users in their wallet, including localized names 733 and logo. 734 Example: ``classpath:/client_metadata.json`` 735 736 737 Database configuration 738 ---------------------- 739 740 Configure the PostgreSQL connection for storing verification state: 741 742 ``spring.datasource.url`` 743 JDBC connection URL for the PostgreSQL database. 744 Example: ``jdbc:postgresql://localhost:5432/verifier_db`` 745 746 ``spring.datasource.username`` 747 Database username for authentication. 748 749 ``spring.datasource.password`` 750 Database password for authentication. 751 752 ``spring.jpa.hibernate.ddl-auto`` 753 Schema management strategy. Leave it at ``validate``, the upstream 754 default: Flyway creates and migrates the schema (``spring.flyway.enabled``, 755 also on by default), and ``validate`` has Hibernate check the result and 756 refuse to start on a mismatch. ``create`` and ``create-drop`` drop the 757 schema and every verification in it on each start, and ``update`` lets 758 Hibernate make changes behind Flyway's back. 759 760 761 Operational parameters 762 ---------------------- 763 764 These settings control verification behavior and resource management: 765 766 ``VERIFICATION_TTL_SEC`` 767 How long a verification request remains valid, in seconds. After this 768 period, the verification expires and the user must start a new request. 769 Default: ``900`` (15 minutes) 770 771 ``DATA_CLEAR_PROCESS_INTERVAL_MS`` 772 Interval between cleanup runs that remove expired verifications from 773 the database, in milliseconds. 774 Default: ``420000`` (7 minutes) 775 776 ``STATUS_LIST_CACHE_TTL_MILLI`` 777 How long to cache credential status list results, in milliseconds. 778 Caching reduces load on the Status Registry. 779 Default: ``900000`` (15 minutes) 780 781 ``ISSUER_PUBLIC_KEY_CACHE_TTL_MILLI`` 782 How long to cache issuer public key lookups, in milliseconds. 783 Caching reduces load on the Base Registry. 784 Default: ``3600000`` (1 hour) 785 786 787 Webhook configuration 788 --------------------- 789 790 Configure webhook callbacks to receive notifications when verification 791 status changes, instead of polling: 792 793 ``webhook.callback-uri`` 794 Full URL where webhook notifications should be sent. If not set, 795 webhooks are disabled and clients must poll for status updates. 796 Example: ``https://kych.example.com/notification`` 797 798 ``webhook.callback-interval`` 799 How often to send queued webhook notifications, in milliseconds. 800 Failed deliveries are retried on subsequent intervals. 801 Default: ``5000`` (5 seconds) 802 803 ``WEBHOOK_API_KEY_HEADER`` 804 HTTP header name for API key authentication on the webhook endpoint. 805 Optional but recommended for production. 806 Example: ``X-API-Key`` 807 808 ``WEBHOOK_API_KEY_VALUE`` 809 The API key value to include in webhook requests. Required if 810 ``WEBHOOK_API_KEY_HEADER`` is set. 811 812 Webhook payloads contain: 813 814 .. code-block:: json 815 816 { 817 "verification_id": "uuid-of-verification", 818 "timestamp": "2025-01-23T10:30:00Z" 819 } 820 821 822 Security configuration 823 ---------------------- 824 825 The Management API can be secured using OAuth 2.0 resource server 826 authentication via Spring Security. 827 828 For fixed public key authentication: 829 830 ``SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_PUBLICKEYLOCATION`` 831 URI path to a public key in PEM format for JWT verification. 832 Example: ``file:/etc/kych/management-api-public-key.pem`` 833 834 For dynamic key discovery via authorization server: 835 836 ``SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUERURI`` 837 URI to the OAuth 2.0 authorization server. The verifier fetches 838 the public key from the server's ``/.well-known/openid-configuration``. 839 840 ``SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWKSETURI`` 841 Direct URI to the JWK Set endpoint, bypassing OpenID Connect discovery. 842 843 ``SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWSALGORITHMS`` 844 Comma-separated list of accepted JWT signature algorithms. 845 Default: ``RS256`` 846 847 848 Example configuration 849 --------------------- 850 851 Complete configuration example for production deployment: 852 853 .. code-block:: yaml 854 855 application: 856 external-url: "https://verifier.example.com" 857 client_id: "did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:abc123" 858 signing-key-verification-method: "did:tdw:QmHash:identifier-reg.trust-infra.swiyu.admin.ch:api:v1:did:abc123#auth-key-01" 859 signing_key: | 860 -----BEGIN EC PRIVATE KEY----- 861 MHcCAQEEIABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstu 862 oAoGCCqGSM49AwEHoUQDQgAE5cice... 863 -----END EC PRIVATE KEY----- 864 client-metadata-file: "classpath:/client_metadata.json" 865 866 spring: 867 datasource: 868 driver-class-name: org.postgresql.Driver 869 url: "jdbc:postgresql://localhost:5432/verifier_db" 870 username: "verifier_user" 871 password: "secure_password_here" 872 jpa: 873 hibernate: 874 ddl-auto: validate 875 876 webhook: 877 callback-uri: "https://kych.example.com/notification" 878 callback-interval: 5000 879 880 881 Running the service 882 =================== 883 884 See the `Deployment Options`_ section for detailed instructions on running 885 the verifier in different environments. This section covers verification 886 and additional runtime configuration. 887 888 889 Verifying the deployment 890 ------------------------ 891 892 After starting, verify the service is running by accessing the Swagger UI: 893 894 ``https://<your-external-url>/swagger-ui/index.html`` 895 896 The Management API endpoints are available under ``/management/api/`` 897 and the OID4VP endpoints under ``/oid4vp/api/``. 898 899 900 Client metadata configuration 901 ----------------------------- 902 903 The ``client_metadata.json`` file defines how your verifier appears to 904 users in their wallet. Create this file with localized display information: 905 906 .. code-block:: json 907 908 { 909 "client_id": "${VERIFIER_DID}", 910 "client_name": "My Verifier Service", 911 "client_name#en": "My Verifier Service", 912 "client_name#de": "Mein Verifizierdienst", 913 "client_name#fr": "Mon service de verification", 914 "logo_uri": "https://example.com/logo.png", 915 "vp_formats": { 916 "jwt_vp": { 917 "alg": ["ES256"] 918 } 919 } 920 } 921 922 The ``vp_formats`` field specifies which Verifiable Presentation formats 923 your verifier accepts. For SD-JWT credentials, ``ES256`` is the standard 924 algorithm. 925 926 927 Integration with KyCH OAuth2 Gateway 928 ==================================== 929 930 The Swiyu Verifier integrates with KyCH OAuth2 Gateway to provide KYC 931 verification for GNU Taler exchanges. This section describes the 932 required configuration on both sides. 933 934 935 KyCH client configuration 936 ------------------------- 937 938 In the KyCH configuration file, configure each client with the Swiyu 939 Verifier URL and webhook endpoint: 940 941 .. code-block:: ini 942 943 [client_exchange] 944 CLIENT_ID = taler-exchange 945 CLIENT_SECRET = secure_secret 946 VERIFIER_URL = https://verifier.example.com 947 VERIFIER_MANAGEMENT_API_PATH = /management/api/verifications 948 REDIRECT_URI = https://exchange.example.com/kyc/callback 949 ACCEPTED_ISSUER_DIDS = {did:tdw:trusted_issuer_did} 950 951 952 Webhook configuration 953 --------------------- 954 955 Configure the Swiyu Verifier to send status notifications to KyCH's 956 notification endpoint: 957 958 .. code-block:: yaml 959 960 webhook: 961 callback-uri: "https://kych.example.com/notification" 962 callback-interval: 5000 963 964 This allows KyCH to receive immediate notification when a verification 965 completes, rather than relying on polling. 966 967 968 Verification flow with KyCH 969 --------------------------- 970 971 When integrated with KyCH, the verification flow is: 972 973 1. KyCH receives an OAuth 2.0 authorization request from the exchange. 974 975 2. KyCH calls ``POST /management/api/verifications`` on the Swiyu Verifier 976 with the credential claims specified in the OAuth scope. 977 978 3. The verifier returns a verification URI and deeplink. 979 980 4. KyCH displays a QR code to the user or provides the deeplink. 981 982 5. The user's Swiyu Wallet scans the QR code and presents the credential. 983 984 6. The Swiyu Verifier validates the credential and sends a webhook to KyCH. 985 986 7. KyCH retrieves the verified claims and continues the OAuth flow. 987 988 989 API Usage 990 ========= 991 992 This section describes how to use the Swiyu Verifier APIs directly, 993 which is useful for understanding the integration or for custom 994 implementations. 995 996 997 Creating a verification request 998 ------------------------------- 999 1000 Create a verification request using the Management API: 1001 1002 .. code-block:: console 1003 1004 $ curl -X POST \ 1005 -H "Content-Type: application/json" \ 1006 -d @request.json \ 1007 https://verifier.example.com/management/api/verifications 1008 1009 Where ``request.json`` contains: 1010 1011 .. code-block:: json 1012 1013 { 1014 "accepted_issuer_dids": ["did:tdw:issuer_did"], 1015 "jwt_secured_authorization_request": true, 1016 "response_mode": "direct_post", 1017 "dcql_query": { 1018 "credentials": [{ 1019 "id": "age_check", 1020 "format": "dc+sd-jwt", 1021 "meta": { "vct_values": ["betaid-sdjwt"] }, 1022 "claims": [{ "path": ["age_over_18"] }], 1023 "require_cryptographic_holder_binding": true 1024 }] 1025 } 1026 } 1027 1028 1029 Request parameters 1030 ^^^^^^^^^^^^^^^^^^ 1031 1032 ``accepted_issuer_dids`` 1033 List of trusted issuer DIDs. Only credentials from these issuers are 1034 accepted. If omitted, credentials from any issuer are accepted (not 1035 recommended for production). 1036 1037 ``trust_anchors`` 1038 Alternative to ``accepted_issuer_dids``. List of trust registry entry 1039 DIDs. The verifier accepts credentials from any issuer trusted by 1040 these anchors. 1041 1042 ``dcql_query`` 1043 **Required.** What the wallet is asked to present, as a Digital 1044 Credentials Query Language query. See the `DCQL query format`_ section 1045 below. 1046 1047 Release 3.0.0 dropped the DIF Presentation Exchange 1048 ``presentation_definition`` that older versions took here, so a request 1049 built for one of those is rejected. 1050 1051 ``jwt_secured_authorization_request`` 1052 Whether to sign the request object. Default: ``true`` (recommended). 1053 1054 ``response_mode`` 1055 How the wallet sends the response. Options: 1056 1057 - ``direct_post``: Unencrypted response (current default) 1058 - ``direct_post.jwt``: JWE-encrypted response (recommended for privacy) 1059 1060 1061 Response 1062 ^^^^^^^^ 1063 1064 .. code-block:: json 1065 1066 { 1067 "id": "verification-uuid", 1068 "request_nonce": "random-nonce", 1069 "state": "PENDING", 1070 "verification_url": "https://verifier.example.com/oid4vp/api/request-object/request-id", 1071 "verification_deeplink": "swiyu-verify://?client_id=..." 1072 } 1073 1074 Store the ``id`` for polling status. Use ``verification_deeplink`` to 1075 generate a QR code for the user to scan. 1076 1077 1078 Polling verification status 1079 --------------------------- 1080 1081 Check the status of a verification: 1082 1083 .. code-block:: shell-session 1084 1085 $ curl -X GET \ 1086 https://verifier.example.com/management/api/verifications/{verification_id} 1087 1088 The response includes the current ``state``: 1089 1090 - ``PENDING``: Waiting for user to present credentials 1091 - ``SUCCESS``: Verification completed successfully 1092 - ``FAILED``: Verification failed (see ``error_code``) 1093 - ``REJECTED``: User rejected the verification request 1094 - ``EXPIRED``: Verification request timed out 1095 1096 1097 DCQL query format 1098 ----------------- 1099 1100 The ``dcql_query`` follows the Digital Credentials Query Language of OpenID4VP 1101 1.0, section 6. Each entry of ``credentials`` asks for one credential: 1102 1103 ``id`` 1104 Names this credential query. The verifier reports the disclosed claims 1105 back under this key in ``wallet_response.dcql_response``, so it is worth 1106 choosing something recognisable; it must match ``^[a-zA-Z0-9_-]+$``. 1107 1108 ``format`` 1109 ``dc+sd-jwt``, the canonical SD-JWT VC media type. The older 1110 ``vc+sd-jwt`` is still accepted during the migration window. 1111 1112 ``meta.vct_values`` 1113 The credential types that satisfy this query, matched against the ``vct`` 1114 claim of the presented credential -- ``betaid-sdjwt`` for the Swiss Beta 1115 ID. This is what lets a wallet holding several credentials tell which one 1116 is being asked for. 1117 1118 ``claims`` 1119 The claims to disclose, one entry per claim, each a *claims path pointer*: 1120 ``{"path": ["age_over_18"]}`` for a top-level claim, and 1121 ``{"path": ["address", "street_address"]}`` for a nested one. Every claim 1122 of the Beta ID is top-level. Leave the member out entirely rather than 1123 sending an empty array, which is rejected. 1124 1125 ``require_cryptographic_holder_binding`` 1126 Whether the wallet must prove it holds the credential's key. Defaults to 1127 true, and there is no good reason to turn it off. 1128 1129 ``trusted_authorities`` 1130 Optional, and normally not sent: when ``accepted_issuer_dids`` is given on 1131 the request, the verifier fills this in from it, so that the wallet can 1132 pre-filter the credentials it offers the user. If you do send it, it has to 1133 agree with ``accepted_issuer_dids``. 1134 1135 KyCH builds exactly such a query from the OAuth 2.0 scope, one claims query 1136 per scope token; ``swiyu-verifier/api_requests/request_over_18.json`` in the 1137 KyCH tree is a complete example in the same shape. 1138 1139 1140 Error codes 1141 =========== 1142 1143 When verification fails, the response includes an error code explaining 1144 the failure: 1145 1146 ``credential_invalid`` 1147 Generic credential validation failure. 1148 1149 ``credential_expired`` 1150 The presented credential has expired. 1151 1152 ``credential_revoked`` 1153 The credential has been revoked by the issuer. 1154 1155 ``credential_suspended`` 1156 The credential is temporarily suspended. 1157 1158 ``credential_missing_data`` 1159 The credential does not contain the requested claims. 1160 1161 ``issuer_not_accepted`` 1162 The credential issuer is not in the accepted issuers list. 1163 1164 ``public_key_of_issuer_unresolvable`` 1165 Could not retrieve the issuer's public key from the Base Registry. 1166 1167 ``unresolvable_status_list`` 1168 Could not check credential revocation status. 1169 1170 ``holder_binding_mismatch`` 1171 The holder could not prove control of the credential. 1172 1173 ``client_rejected`` 1174 The user rejected the verification request in their wallet. 1175 1176 ``jwt_expired`` 1177 A JWT in the verification process has expired. 1178 1179 ``invalid_format`` 1180 The credential or presentation format is invalid. 1181 1182 1183 Deployment considerations 1184 ========================= 1185 1186 Production security 1187 ------------------- 1188 1189 For production deployments: 1190 1191 1. **Protect the Management API**: Use network segmentation or OAuth 2.0 1192 authentication to restrict access to internal systems only. 1193 1194 2. **Use an API Gateway**: Place a WAF or API gateway in front of the 1195 OID4VP endpoints to filter malicious requests. 1196 1197 3. **Enable HTTPS**: The OID4VP endpoints must use HTTPS with a valid 1198 certificate from a trusted CA. 1199 1200 4. **Secure credentials**: Store the signing key securely. Consider using 1201 an HSM for production key management. 1202 1203 5. **Set accepted issuers**: Always specify ``accepted_issuer_dids`` or 1204 ``trust_anchors`` in verification requests. 1205 1206 1207 HSM support 1208 ----------- 1209 1210 For production deployments, the signing key can be stored in a Hardware 1211 Security Module (HSM) instead of the configuration file. Configure: 1212 1213 ``SIGNING_KEY_MANAGEMENT_METHOD`` 1214 Set to ``pkcs11`` for Sun PKCS#11 provider or a vendor-specific value. 1215 1216 ``HSM_HOST``, ``HSM_PORT``, ``HSM_USER``, ``HSM_PASSWORD`` 1217 Connection parameters for the HSM. 1218 1219 ``HSM_KEY_ID`` 1220 Key identifier or alias within the HSM. 1221 1222 See the Swiyu Verifier documentation for vendor-specific HSM configuration. 1223 1224 1225 Monitoring 1226 ---------- 1227 1228 The verifier exposes Prometheus metrics at ``/actuator/prometheus``. 1229 Enable authentication for this endpoint in production: 1230 1231 .. code-block:: yaml 1232 1233 MONITORING_BASIC_AUTH_ENABLED: true 1234 MONITORING_BASIC_AUTH_USERNAME: prometheus 1235 MONITORING_BASIC_AUTH_PASSWORD: secure_password 1236 1237 1238 See also 1239 ======== 1240 1241 - kych.conf(5) - KyCH OAuth2 Gateway configuration 1242 - kych-oauth2-gateway(1) - KyCH OAuth2 Gateway server 1243 - `Swiyu Trust Infrastructure <https://swiyu-admin-ch.github.io/>`_ 1244 - `OID4VP Specification <https://openid.net/specs/openid-4-verifiable-presentations-1_0.html>`_ 1245 - `DCQL, in OpenID for Verifiable Presentations 1.0 <https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-6>`_ 1246 1247 1248 Bugs 1249 ==== 1250 1251 Report bugs by using https://bugs.taler.net/ or by sending electronic 1252 mail to <taler@gnu.org>.