kych

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

taler-kych-manual.rst (23533B)


      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 KyCH Operator Manual
     19 ####################
     20 
     21 Introduction
     22 ============
     23 
     24 About KyCH
     25 ----------
     26 
     27 KyCH is an OAuth 2.0-compatible gateway for KYC (Know Your Customer) verification
     28 using OID4VP (OpenID for Verifiable Presentations) with Swiyu Verifier.
     29 By redirecting a user-agent to a KyCH service, a client (such as a Taler exchange)
     30 can have KyCH verify the user's identity through verifiable credentials stored
     31 in the user's Swiyu Wallet and obtain the verified identity attributes via
     32 the ``/info`` endpoint.
     33 
     34 
     35 About this manual
     36 -----------------
     37 
     38 This manual targets system administrators who want to install,
     39 operate or integrate a KyCH service.  To report issues
     40 or learn about known limitations, please check our
     41 `bug tracker <https://bugs.taler.net>`__.
     42 
     43 
     44 Architecture overview
     45 ---------------------
     46 
     47 The following diagram shows the high-level architecture of KyCH and its
     48 interactions with other components:
     49 
     50 .. image:: images/kych_overview.jpg
     51    :alt: KyCH Architecture Overview
     52    :align: center
     53 
     54 KyCH acts as an OAuth 2.0 gateway that orchestrates KYC verification between:
     55 
     56 - **Taler Exchange**: The client that requires KYC verification for users.
     57 - **KyCH OAuth2 Gateway**: Manages OAuth 2.0 flows and verification sessions.
     58 - **Swiyu Verifier**: Handles OID4VP verification requests.
     59 - **Swiyu Wallet**: The user's mobile wallet containing verifiable credentials.
     60 
     61 The flow proceeds as follows:
     62 
     63 1. The *resource owner* (user) initiates a KYC-requiring operation with the
     64    *client* (e.g., Taler exchange).
     65 
     66 2. The *client* calls ``POST /setup/{client_id}`` with its client secret to
     67    obtain a ``nonce`` for the verification session.
     68 
     69 3. The *client* redirects the user-agent to ``GET /authorize/{nonce}`` with
     70    OAuth 2.0 parameters (``response_type``, ``client_id``, ``redirect_uri``,
     71    ``state``, ``scope``).
     72 
     73 4. KyCH creates a verification request with the Swiyu Verifier and returns
     74    an HTML page with a QR code (or JSON with ``verification_url``).
     75 
     76 5. The user scans the QR code with their Swiyu Wallet, which retrieves the
     77    presentation definition and requests user consent.
     78 
     79 6. Upon user consent, the Swiyu Wallet presents the requested credentials to
     80    the Swiyu Verifier.
     81 
     82 7. The Swiyu Verifier sends a webhook notification to KyCH's ``/notification``
     83    endpoint with the verification result.
     84 
     85 8. The client polls ``GET /status/{verification_id}`` until the status becomes
     86    ``verified``, then redirects to ``GET /finalize/{verification_id}``.
     87 
     88 9. KyCH redirects the user to the client's ``redirect_uri`` with an
     89    authorization code.
     90 
     91 10. The client exchanges the authorization code for an access token via
     92     ``POST /token``.
     93 
     94 11. The client retrieves the verified identity claims via ``GET /info`` using
     95     the access token.
     96 
     97 
     98 .. _KyCHInstallation:
     99 
    100 Installation
    101 ============
    102 
    103 Prerequisites
    104 -------------
    105 
    106 Before installing KyCH, ensure you have the following:
    107 
    108 * Rust toolchain (stable, version 1.70 or later)
    109 * PostgreSQL 12 or later
    110 * A Swiyu Verifier instance with API access
    111 
    112 
    113 Building from source
    114 --------------------
    115 
    116 Clone the KyCH repository and build the release binaries:
    117 
    118 .. code-block:: shell-session
    119 
    120    $ git clone https://github.com/example/kych.git
    121    $ cd kych/kych_oauth2_gateway
    122    $ cargo build --release
    123 
    124 The compiled binaries will be available in ``target/release/``:
    125 
    126 * ``kych-oauth2-gateway`` - the main HTTP server
    127 * ``kych-client-management`` - CLI tool for managing clients
    128 
    129 
    130 Database setup
    131 --------------
    132 
    133 KyCH uses PostgreSQL to store client configurations, verification sessions,
    134 authorization codes, and access tokens.
    135 
    136 First, switch to the ``postgres`` user and create a database user and database
    137 for KyCH:
    138 
    139 .. code-block:: shell-session
    140 
    141    [root@server]# su - postgres
    142    [postgres@server]# createuser kych
    143    [postgres@server]# createdb -O kych kych
    144    [postgres@server]# exit
    145 
    146 The ``createuser`` command creates a PostgreSQL role named ``kych``.
    147 The ``createdb`` command creates a database named ``kych`` owned by the
    148 ``kych`` role (``-O kych``).
    149 
    150 Next, initialize the database schema. KyCH uses a versioning system to manage
    151 database migrations. First, install the versioning support, then apply the
    152 schema migration:
    153 
    154 .. code-block:: shell-session
    155 
    156    $ psql -U kych -d kych -f oauth2_gatewaydb/versioning.sql
    157    $ psql -U kych -d kych -f oauth2_gatewaydb/oauth2gw-0001.sql
    158 
    159 The ``versioning.sql`` script installs the ``_v`` schema which tracks applied
    160 database patches. The ``oauth2gw-0001.sql`` script creates the ``oauth2gw``
    161 schema with the following tables:
    162 
    163 - ``clients``: Registered OAuth 2.0 clients and their configurations.
    164 - ``verification_sessions``: Active and completed verification sessions.
    165 - ``authorization_codes``: OAuth 2.0 authorization codes issued after verification.
    166 - ``access_tokens``: Bearer tokens issued to clients for accessing user data.
    167 
    168 .. note::
    169 
    170    The SQL migration files are located in the ``oauth2_gatewaydb/`` directory
    171    of the KyCH source tree. Adjust the path if you installed KyCH to a
    172    different location.
    173 
    174 
    175 Configuration
    176 =============
    177 
    178 Configuration file location
    179 ---------------------------
    180 
    181 KyCH reads its configuration from a file in INI format. The default location
    182 is ``/etc/kych/kych.conf``. You can specify an alternative path using the
    183 ``--config`` or ``-c`` command-line option.
    184 
    185 
    186 Main configuration section
    187 --------------------------
    188 
    189 The main configuration is specified in the ``[kych-oauth2-gateway]`` section:
    190 
    191 .. code-block:: ini
    192    :caption: /etc/kych/kych.conf
    193 
    194    [kych-oauth2-gateway]
    195    # Server binding - choose either TCP or Unix socket (not both)
    196 
    197    # For TCP binding:
    198    #HOST = 127.0.0.1
    199    #PORT = 8080
    200 
    201    # For Unix socket binding:
    202    UNIXPATH = /run/kych/kych.sock
    203    UNIXPATH_MODE = 666
    204 
    205    # Database connection string
    206    DATABASE = postgres://kych:password@localhost/kych
    207 
    208    # Cryptographic parameters
    209    NONCE_BYTES = 32
    210    TOKEN_BYTES = 32
    211    AUTH_CODE_BYTES = 32
    212    AUTH_CODE_TTL_MINUTES = 10
    213 
    214    # Optional: restrict allowed OAuth scopes
    215    #ALLOWED_SCOPES = {family_name, given_name, birth_date}
    216 
    217 
    218 Server binding options
    219 ----------------------
    220 
    221 KyCH can listen on either a TCP socket or a Unix domain socket, but not both
    222 simultaneously. For production deployments behind a reverse proxy (recommended),
    223 Unix sockets provide better security by avoiding network exposure.
    224 
    225 **TCP binding:**
    226 
    227 Use TCP binding for development or when KyCH must be accessible over the network.
    228 
    229 -  ``HOST``: The IP address to bind to. Use ``127.0.0.1`` for localhost-only
    230    access or ``0.0.0.0`` to accept connections on all interfaces.
    231 -  ``PORT``: The TCP port to listen on (e.g., ``8080``).
    232 
    233 **Unix socket binding:**
    234 
    235 Use Unix sockets for production deployments behind nginx or another reverse proxy.
    236 This avoids exposing KyCH directly to the network.
    237 
    238 -  ``UNIXPATH``: Path to the Unix domain socket file (e.g., ``/run/kych/kych.sock``).
    239    Ensure the directory exists and is writable by the KyCH process.
    240 -  ``UNIXPATH_MODE``: Octal permission mode for the socket file (default: ``666``).
    241    Set to ``660`` if only the reverse proxy user needs access.
    242 
    243 
    244 Database configuration
    245 ----------------------
    246 
    247 KyCH requires a PostgreSQL database to persist client registrations, verification
    248 sessions, authorization codes, and access tokens. The connection is specified
    249 using a standard PostgreSQL connection URI.
    250 
    251 -  ``DATABASE``: PostgreSQL connection string in the format
    252    ``postgres://user:password@host:port/database``. For local connections,
    253    you can omit the port (defaults to 5432). Example:
    254    ``postgres://kych:secretpassword@localhost/kych``
    255 
    256 For production deployments, consider using environment variables or a secrets
    257 manager to avoid storing database credentials in plain text configuration files.
    258 
    259 
    260 Cryptographic parameters
    261 ------------------------
    262 
    263 KyCH generates cryptographically secure random values for nonces, access tokens,
    264 and authorization codes. These parameters control the size (entropy) and
    265 lifetime of these values. Larger sizes provide more security but result in
    266 longer token strings.
    267 
    268 -  ``NONCE_BYTES``: Number of random bytes for session nonces. The nonce is
    269    used in the ``/authorize/{nonce}`` URL to identify the verification session.
    270    Recommended: ``32`` (256 bits of entropy, base64-encoded to ~43 characters).
    271 
    272 -  ``TOKEN_BYTES``: Number of random bytes for OAuth 2.0 access tokens. These
    273    tokens are used by clients to access the ``/info`` endpoint.
    274    Recommended: ``32`` (256 bits of entropy).
    275 
    276 -  ``AUTH_CODE_BYTES``: Number of random bytes for OAuth 2.0 authorization codes.
    277    These short-lived codes are exchanged for access tokens at the ``/token``
    278    endpoint. Recommended: ``32`` (256 bits of entropy).
    279 
    280 -  ``AUTH_CODE_TTL_MINUTES``: How long authorization codes remain valid before
    281    expiring. Clients must exchange the code for an access token within this
    282    time window. Default: ``10`` minutes. Shorter values are more secure but
    283    may cause issues with slow clients.
    284 
    285 
    286 Verifiable Credential settings
    287 ------------------------------
    288 
    289 These settings define the type of verifiable credential that KyCH will request
    290 from the Swiyu Verifier during the OID4VP flow. The configuration must match
    291 the credential type supported by your Swiyu Verifier deployment and the
    292 credentials available in users' Swiyu Wallets.
    293 
    294 .. code-block:: ini
    295    :caption: /etc/kych/kych.conf
    296 
    297    [kych-oauth2-gateway]
    298    VC_TYPE = betaid-sdjwt
    299    VC_FORMAT = vc+sd-jwt
    300    VC_ALGORITHMS = {ES256}
    301    VC_CLAIMS = {family_name, given_name, birth_date, age_over_18}
    302 
    303 -  ``VC_TYPE``: The verifiable credential type identifier. This must match
    304    the credential type configured in the Swiyu Verifier. For Swiss Beta ID
    305    credentials, use ``betaid-sdjwt``.
    306 
    307 -  ``VC_FORMAT``: The credential format. SD-JWT (Selective Disclosure JWT)
    308    credentials use ``vc+sd-jwt``, which allows users to disclose only the
    309    specific claims requested.
    310 
    311 -  ``VC_ALGORITHMS``: Cryptographic signature algorithms accepted for credential
    312    verification, as a bracketed list. The Swiyu ecosystem uses ``{ES256}``
    313    (ECDSA with P-256 and SHA-256).
    314 
    315 -  ``VC_CLAIMS``: The complete set of claims that may be requested from the
    316    credential. Clients specify which of these claims they need via the
    317    ``scope`` parameter in the authorization request. Only claims listed here
    318    can be requested.
    319 
    320 **Available claims for Swiss Beta ID (betaid-sdjwt):**
    321 
    322 The Swiss Beta ID credential supports the following claims:
    323 
    324 *Personal identification:*
    325 
    326 - ``family_name``: Family name (surname)
    327 - ``given_name``: Given name (first name)
    328 - ``birth_date``: Date of birth (ISO 8601 format)
    329 - ``sex``: Gender
    330 - ``portrait``: Photograph of the credential holder
    331 
    332 *Swiss-specific attributes:*
    333 
    334 - ``place_of_origin``: Place of citizenship (Heimatort), a Swiss legal concept
    335 - ``birth_place``: Place of birth
    336 - ``nationality``: Nationality/citizenship
    337 - ``personal_administrative_number``: Swiss social security number (AHV/AVS number)
    338 
    339 *Age verification (selective disclosure):*
    340 
    341 - ``age_over_16``: Boolean indicating if holder is 16 or older
    342 - ``age_over_18``: Boolean indicating if holder is 18 or older
    343 - ``age_over_65``: Boolean indicating if holder is 65 or older
    344 - ``age_birth_year``: Year of birth (for age verification without full date)
    345 
    346 *Document information:*
    347 
    348 - ``document_number``: Identity document number
    349 - ``issuance_date``: When the credential was issued
    350 - ``expiry_date``: When the credential expires
    351 - ``issuing_authority``: Authority that issued the credential
    352 - ``issuing_country``: Country that issued the credential
    353 
    354 *Verification metadata:*
    355 
    356 - ``verification_type``: How the identity was verified (e.g., in-person)
    357 - ``verification_organization``: Organization that performed verification
    358 - ``reference_id_type``: Type of reference identity document
    359 - ``reference_id_expiry_date``: Expiry date of reference document
    360 - ``additional_person_info``: Additional personal information
    361 
    362 For most KYC use cases, requesting ``family_name``, ``given_name``,
    363 ``birth_date``, and ``age_over_18`` provides sufficient identity verification
    364 while minimizing data collection.
    365 
    366 
    367 Scope restrictions
    368 ------------------
    369 
    370 Optionally, you can restrict which claims clients are allowed to request,
    371 regardless of what claims are defined in ``VC_CLAIMS``. This provides an
    372 additional policy layer to limit data exposure.
    373 
    374 -  ``ALLOWED_SCOPES``: If set, only these claims can be requested by clients.
    375    If not set, clients may request any claim from ``VC_CLAIMS``. Format is
    376    a bracketed comma-separated list.
    377 
    378 For example, to allow clients to only verify age without accessing personal
    379 details:
    380 
    381 .. code-block:: ini
    382 
    383    ALLOWED_SCOPES = {age_over_18, age_over_16}
    384 
    385 
    386 Client Management
    387 =================
    388 
    389 OAuth 2.0 clients (such as Taler exchanges) must be registered with KyCH before
    390 they can initiate KYC verification flows. Each client has its own credentials,
    391 redirect URI, and may use a different Swiyu Verifier instance.
    392 
    393 There are two methods to manage clients:
    394 
    395 - **Configuration-based**: Define clients in the configuration file and use the
    396   ``sync`` command to load them into the database. Good for infrastructure-as-code
    397   deployments.
    398 
    399 - **CLI-based**: Use the ``kych-client-management`` tool to create, update, and
    400   delete clients directly in the database. Good for dynamic management.
    401 
    402 
    403 Configuration-based client management
    404 -------------------------------------
    405 
    406 Clients can be defined in the configuration file using ``[client_*]`` sections.
    407 Each section name must start with ``client_`` followed by a unique identifier
    408 (e.g., ``[client_exchange]``, ``[client_staging]``).
    409 
    410 .. code-block:: ini
    411    :caption: /etc/kych/kych.conf
    412 
    413    [client_exchange]
    414    CLIENT_ID = exchange_production
    415    CLIENT_SECRET = secret-token:your-secure-secret-here
    416    VERIFIER_URL = https://swiyu-verifier.example.com
    417    VERIFIER_MANAGEMENT_API_PATH = /management/api/verifications
    418    REDIRECT_URI = https://exchange.example.com/kyc/kych-redirect
    419    ACCEPTED_ISSUER_DIDS = {did:tdw:trusted_issuer_1, did:tdw:trusted_issuer_2}
    420 
    421 **Client configuration options:**
    422 
    423 -  ``CLIENT_ID``: Unique identifier that the client uses to authenticate with
    424    KyCH. This is passed in the ``client_id`` parameter during OAuth 2.0 flows
    425    and in the ``/setup/{client_id}`` endpoint URL.
    426 
    427 -  ``CLIENT_SECRET``: Shared secret for client authentication. The client
    428    provides this as a Bearer token in the ``Authorization`` header when calling
    429    ``/setup``, and in the request body when calling ``/token``. Use the
    430    ``secret-token:`` prefix per RFC 8959 for secrets that should not be logged.
    431    KyCH stores the secret as a bcrypt hash in the database.
    432 
    433 -  ``VERIFIER_URL``: Base URL of the Swiyu Verifier instance that this client
    434    will use for credential verification. Different clients can use different
    435    verifier instances (e.g., production vs. staging).
    436 
    437 -  ``VERIFIER_MANAGEMENT_API_PATH``: Path to the verifier's management API
    438    endpoint for creating verification requests. Default:
    439    ``/management/api/verifications``. Only change this if your Swiyu Verifier
    440    uses a non-standard API path.
    441 
    442 -  ``REDIRECT_URI``: The OAuth 2.0 redirect URI where users are sent after
    443    completing verification. This must exactly match the ``redirect_uri``
    444    parameter provided by the client in authorization requests. For security,
    445    KyCH rejects requests with mismatched redirect URIs.
    446 
    447 -  ``ACCEPTED_ISSUER_DIDS``: List of trusted credential issuer DIDs (Decentralized
    448    Identifiers) in bracketed format. Only credentials issued by these DIDs will
    449    be accepted. This provides trust anchoring - you specify which issuers you
    450    trust to have properly verified identities. Example:
    451    ``{did:tdw:issuer1, did:tdw:issuer2}``.
    452 
    453 
    454 CLI-based client management
    455 ---------------------------
    456 
    457 The ``kych-client-management`` tool manages clients directly in the database.
    458 All commands require the ``--config`` option to specify the configuration file
    459 (for database connection settings).
    460 
    461 **Listing clients:**
    462 
    463 Display all registered clients:
    464 
    465 .. code-block:: shell-session
    466 
    467    $ kych-client-management --config /etc/kych/kych.conf list
    468 
    469 This shows a summary of all clients including their IDs and verifier URLs.
    470 
    471 **Viewing client details:**
    472 
    473 Show full details for a specific client:
    474 
    475 .. code-block:: shell-session
    476 
    477    $ kych-client-management --config /etc/kych/kych.conf show exchange_prod
    478 
    479 This displays all configuration options for the client, except the secret
    480 (which is stored as a hash).
    481 
    482 **Creating a client:**
    483 
    484 Register a new OAuth 2.0 client:
    485 
    486 .. code-block:: shell-session
    487 
    488    $ kych-client-management --config /etc/kych/kych.conf create \
    489        --client-id exchange_prod \
    490        --secret "secret-token:your-secure-secret" \
    491        --verifier-url https://swiyu-verifier.example.com \
    492        --redirect-uri https://exchange.example.com/kyc/kych-redirect \
    493        --accepted-issuer-dids "{did:tdw:trusted_issuer}"
    494 
    495 The ``--client-id``, ``--secret``, ``--verifier-url``, and ``--redirect-uri``
    496 options are required. The ``--verifier-api-path`` defaults to
    497 ``/management/api/verifications`` if not specified.
    498 
    499 **Updating a client:**
    500 
    501 Modify an existing client's configuration:
    502 
    503 .. code-block:: shell-session
    504 
    505    $ kych-client-management --config /etc/kych/kych.conf update exchange_prod \
    506        --redirect-uri https://new-exchange.example.com/kyc/kych-redirect
    507 
    508 Only the specified options are updated; other settings remain unchanged.
    509 
    510 **Deleting a client:**
    511 
    512 Remove a client from the database:
    513 
    514 .. code-block:: shell-session
    515 
    516    $ kych-client-management --config /etc/kych/kych.conf delete exchange_prod
    517 
    518 You will be prompted for confirmation. Use ``-y`` to skip the prompt:
    519 
    520 .. code-block:: shell-session
    521 
    522    $ kych-client-management --config /etc/kych/kych.conf delete exchange_prod -y
    523 
    524 .. warning::
    525 
    526    Deleting a client will cascade to all associated sessions and tokens.
    527 
    528 **Synchronizing configuration to database:**
    529 
    530 The ``sync`` command is the bridge between configuration-based and database-based
    531 client management. It reads all ``[client_*]`` sections from the configuration
    532 file and creates or updates the corresponding clients in the database:
    533 
    534 .. code-block:: shell-session
    535 
    536    $ kych-client-management --config /etc/kych/kych.conf sync
    537 
    538 This is useful for infrastructure-as-code workflows where client configuration
    539 is managed in version control and deployed to the database during releases.
    540 
    541 To also remove clients from the database that are no longer defined in the
    542 configuration file, use the ``--prune`` flag:
    543 
    544 .. code-block:: shell-session
    545 
    546    $ kych-client-management --config /etc/kych/kych.conf sync --prune
    547 
    548 .. warning::
    549 
    550    The ``--prune`` flag will delete clients and all their associated sessions
    551    and tokens. Use with caution in production.
    552 
    553 
    554 Deployment
    555 ==========
    556 
    557 systemd service
    558 ---------------
    559 
    560 Create a systemd service file for KyCH:
    561 
    562 .. code-block:: ini
    563    :caption: /etc/systemd/system/kych.service
    564 
    565    [Unit]
    566    Description=KyCH OAuth2 Gateway
    567    After=network.target postgresql.service
    568 
    569    [Service]
    570    Type=simple
    571    User=kych
    572    Group=kych
    573    ExecStart=/usr/local/bin/kych-oauth2-gateway --config /etc/kych/kych.conf
    574    Restart=always
    575    RestartSec=5
    576 
    577    [Install]
    578    WantedBy=multi-user.target
    579 
    580 Enable and start the service:
    581 
    582 .. code-block:: shell-session
    583 
    584    [root@server]# systemctl daemon-reload
    585    [root@server]# systemctl enable kych
    586    [root@server]# systemctl start kych
    587 
    588 
    589 Reverse proxy setup
    590 -------------------
    591 
    592 KyCH should be deployed behind a reverse proxy that provides TLS termination,
    593 as required by OAuth 2.0. The following example shows an nginx configuration
    594 using a Unix socket:
    595 
    596 .. code-block:: nginx
    597    :caption: /etc/nginx/sites-available/kych
    598 
    599    upstream kych {
    600        server unix:/run/kych/kych.sock;
    601    }
    602 
    603    server {
    604        listen 443 ssl http2;
    605        server_name kych.example.com;
    606 
    607        ssl_certificate /etc/letsencrypt/live/kych.example.com/fullchain.pem;
    608        ssl_certificate_key /etc/letsencrypt/live/kych.example.com/privkey.pem;
    609 
    610        location / {
    611            proxy_pass http://kych;
    612            proxy_set_header Host $host;
    613            proxy_set_header X-Real-IP $remote_addr;
    614            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    615            proxy_set_header X-Forwarded-Proto $scheme;
    616        }
    617    }
    618 
    619 Enable the site:
    620 
    621 .. code-block:: shell-session
    622 
    623    [root@server]# ln -s /etc/nginx/sites-available/kych /etc/nginx/sites-enabled/kych
    624    [root@server]# nginx -t
    625    [root@server]# systemctl reload nginx
    626 
    627 
    628 Webhook configuration
    629 ---------------------
    630 
    631 KyCH receives callbacks from the Swiyu Verifier when a verification request
    632 changes state (e.g., when the user presents their credential). The webhook
    633 endpoint is ``/notification``.
    634 
    635 When configuring the Swiyu Verifier, set the callback URL to:
    636 
    637 ::
    638 
    639    https://kych.example.com/notification
    640 
    641 This endpoint must be accessible from the Swiyu Verifier service.
    642 
    643 .. note::
    644 
    645    The Swiyu Verifier must be configured to send notifications to the KyCH
    646    gateway. Consult the Swiyu Verifier documentation for details on configuring
    647    webhook callbacks. The verifier will POST a JSON payload containing the
    648    ``verification_id`` and ``timestamp`` when a verification completes.
    649 
    650 
    651 Integration with Taler Exchange
    652 ===============================
    653 
    654 To use KyCH as a KYC provider for a GNU Taler exchange, configure the
    655 exchange with the following settings:
    656 
    657 .. code-block:: ini
    658    :caption: /etc/taler-exchange/conf.d/exchange-kyc.conf
    659 
    660    [kyc-provider-kych]
    661    LOGIC = oauth2
    662    PROVIDED_CHECKS = KYCH_IDENTITY
    663    KYC_OAUTH2_AUTHORIZE_URL = https://kych.example.com/authorize
    664    KYC_OAUTH2_TOKEN_URL = https://kych.example.com/token
    665    KYC_OAUTH2_INFO_URL = https://kych.example.com/info
    666    KYC_OAUTH2_CLIENT_ID = exchange_production
    667    KYC_OAUTH2_CLIENT_SECRET = secret-token:your-secure-secret
    668    KYC_OAUTH2_POST_URL = https://kych.example.com
    669 
    670 The exchange will then use KyCH for KYC verification when the
    671 ``KYCH_IDENTITY`` check is required.
    672 
    673 
    674 OAuth 2.0 endpoints
    675 -------------------
    676 
    677 KyCH provides the following OAuth 2.0 endpoints:
    678 
    679 -  ``/authorize`` - Authorization endpoint (redirects user to Swiyu Wallet flow)
    680 -  ``/token`` - Token endpoint (exchanges authorization code for access token)
    681 -  ``/info`` - Info endpoint (returns verified identity claims)
    682 
    683 The flow follows standard OAuth 2.0 authorization code grant:
    684 
    685 1. Client redirects user to ``/authorize`` with ``client_id``, ``redirect_uri``,
    686    ``response_type=code``, and optional ``state`` parameter.
    687 
    688 2. User completes verification via Swiyu Wallet.
    689 
    690 3. User is redirected to the client's ``redirect_uri`` with an authorization
    691    ``code``.
    692 
    693 4. Client exchanges the ``code`` for an ``access_token`` at ``/token``.
    694 
    695 5. Client retrieves user information using the ``access_token`` at ``/info``.