kych

OAuth 2.0 API for Swiyu to enable Taler integration of Swiyu for KYC (experimental)
Log | Files | Refs | README

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>.