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