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