summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2024-04-19 16:41:01 +0200
committerChristian Grothoff <christian@grothoff.org>2024-04-19 16:41:01 +0200
commit0fc5dd62d57b722bb1e2b29fc99aa919d6c89324 (patch)
treec9b9b2f5cf66c34f54bbc31194f384594235e890
parent67e6516a51af264e260490b596b9ab7c6d100e63 (diff)
downloaddocs-0fc5dd62d57b722bb1e2b29fc99aa919d6c89324.tar.gz
docs-0fc5dd62d57b722bb1e2b29fc99aa919d6c89324.tar.bz2
docs-0fc5dd62d57b722bb1e2b29fc99aa919d6c89324.zip
improve KYC spec
-rw-r--r--design-documents/023-taler-kyc.rst273
1 files changed, 190 insertions, 83 deletions
diff --git a/design-documents/023-taler-kyc.rst b/design-documents/023-taler-kyc.rst
index 0ecfc4aa..77dbe2bb 100644
--- a/design-documents/023-taler-kyc.rst
+++ b/design-documents/023-taler-kyc.rst
@@ -240,10 +240,43 @@ Terminology
* **Type of operation**: The operation type determines which Taler-specific operation has triggered the KYC requirement. We support four types of operation: withdraw (by customer), deposit (by merchant), P2P receive (by wallet) and (high) wallet balance.
+451 Response
+^^^^^^^^^^^^
+
+When KYC operations are required, various endpoints may respond with a
+``451 Unavailable for Legal Reasons`` status code and a `KycNeededRedirect`
+body.
+
+ .. ts:def:: KycNeededRedirect
+
+ interface KycNeededRedirect {
+
+ // Numeric `error code <error-codes>` unique to the condition.
+ // Should always be ``TALER_EC_EXCHANGE_GENERIC_KYC_REQUIRED``.
+ code: number;
+
+ // Human-readable description of the error, i.e. "missing parameter", "commitment violation", ...
+ // Should give a human-readable hint about the error's nature. Optional, may change without notice!
+ hint?: string;
+
+ // Public key associated with the account. The client must sign
+ // the initial request for the KYC status using the corresponding
+ // private key. Will be either a reserve public key or a merchant
+ // (instance) public key.
+ account_pub: EddsaPublicKey;
+
+ // Legitimization target that the merchant should
+ // use to check for its KYC status using
+ // the ``/kyc-check/$REQUIREMENT_ROW`` endpoint.
+ requirement_row: Integer;
+
+ }
+
+
New Endpoints
^^^^^^^^^^^^^
-.. http:get:: /kyc-check/$REQUIREMENT_ROW/$H_PAYTO
+.. http:get:: /kyc-check/$REQUIREMENT_ROW
Checks the KYC status of a particular payment target and possibly begins the
KYC process. This endpoint is typically used by wallets or merchants that
@@ -251,27 +284,28 @@ New Endpoints
requirement has been fulfilled. Long-polling may be used to instantly
observe a change in the KYC requirement status.
- The ``/kyc-check/`` endpoint is based on the legitimization measure's
- serial number. It is returned in `KycNeededRedirect` responses via
- the ``requirement_row`` field together with the ``h_payto``.
- Access is *authenticated* by also passing the hash of the payto://-URI.
+ The requirement row of the ``/kyc-check/`` endpoint encodes the
+ legitimization measure's serial number. It is returned in
+ `KycNeededRedirect` responses via the ``requirement_row`` field.
- .. note::
-
- Weak authentication is acceptable, as the KYC status or the ability to
- initiate a KYC process are not very sensitive.
-
- Given a valid pair of requirement row and payto-hash, the ``/kyc-check/``
- endpoint returns either just the KYC status or redirects the client (202) to
- the next required stage of the KYC process. The redirection must be for an
- HTTP(S) endpoint to be triggered via a simple HTTP GET. It must always be
- the same endpoint for the same client, as the wallet/merchant backend are
- not required to check for changes to this endpoint. Clients that received a
- 202 status code may repeat the request and use long-polling to detect a
- change of the HTTP status.
+ Given a valid pair of requirement row and account owner signature, the
+ ``/kyc-check/`` endpoint returns either just the KYC status or redirects the
+ client (202) to the next required stage of the KYC process. The redirection
+ must be for an HTTP(S) endpoint to be triggered via a simple HTTP GET. It
+ must always be the same endpoint for the same client, as the wallet/merchant
+ backend are not required to check for changes to this endpoint. Clients
+ that received a 202 status code may repeat the request and use long-polling
+ to detect a change of the HTTP status.
**Request:**
+ *Account-Owner-Signature*: The client must provide Base-32 encoded EdDSA
+ signature with ``$ACCOUNT_PRIV``, affirming the desire to obtain KYC data.
+ Note that this is merely a simple authentication mechanism, the details of
+ the request are not protected by the signature. The ``$ACCOUNT_PRIV``
+ is either the (wallet long-term) reserve private key or the merchant
+ instance private key.
+
:query timeout_ms=NUMBER: *Optional.* If specified, the exchange will
wait up to ``timeout_ms`` milliseconds if the requirement continues
to be mandatory provisioning of KYC data by the client.
@@ -380,31 +414,32 @@ New Endpoints
}
-.. http:get:: /kyc-spa/{$HASH,$FILENAME}
-
- A set of ``/kyc-spa/$HASH`` GET endpoints is created per client ``$HASH``
- that serves the KYC SPA. This is where the ``/kyc-check/`` endpoint will
- redirect clients unless all KYC/AML requirements are satisfied. The KYC SPA
- will use the ``$HASH`` of its URL to initialize itself via the
- ``/kyc-info/$HASH`` endpoint family. The KYC SPA may download additional
- resources via ``/kyc-spa/$FILENAME``. The filenames must not match
- base32-encoded SHA-512 hashes.
-
-.. http:get:: /kyc-info/$HASH
-
- A new set of ``/kyc-info/$HASH`` GET endpoints is created per client
- ``$HASH`` to return information about the state of the KYC or AML process to
- the KYC SPA. The SPA uses this information to show the user an appropriate
- dialog. The SPA should also long-poll this endpoint for changes to the AML/KYC
- state. Note that this is a client-facing endpoint, so it will only provide a
- restricted amount of information to the customer (as some laws may forbid us
- to inform particular customers about their true status). The endpoint will
- typically inform the SPA about possible choices to proceed, such as directly
- uploading files, contacting AML staff, or proceeding with a particular KYC
- process at an external provider (such as Challenger). If the user chooses to
- initate a KYC process at an external provider, the SPA must request the
- respective process to be set-up by the exchange via the ``/kyc-start/``
- endpoint.
+.. http:get:: /kyc-spa/$TARGET_TOKEN
+.. http:get:: /kyc-spa/$FILENAME
+
+ A set of ``/kyc-spa/$TARGET_TOKEN`` GET endpoints is created per account
+ hash that serves the KYC SPA. This is where the ``/kyc-check/`` endpoint
+ will in principle redirect clients. The KYC SPA will use the
+ ``$TARGET_TOKEN`` of its URL to initialize itself via the
+ ``/kyc-info/$TARGET_TOKEN`` endpoint family. The KYC SPA may download
+ additional resources via ``/kyc-spa/$FILENAME``. The filenames must not
+ match base32-encoded 256-bit values.
+
+.. http:get:: /kyc-info/$TARGET_TOKEN
+
+ A new set of ``/kyc-info/$TARGET_TOKEN`` GET endpoints is created per client
+ account hash to return information about the state of the KYC or AML process
+ to the KYC SPA. The SPA uses this information to show the user an
+ appropriate dialog. The SPA should also long-poll this endpoint for changes
+ to the AML/KYC state. Note that this is a client-facing endpoint, so it will
+ only provide a restricted amount of information to the customer (as some
+ laws may forbid us to inform particular customers about their true status).
+ The endpoint will typically inform the SPA about possible choices to
+ proceed, such as directly uploading files, contacting AML staff, or
+ proceeding with a particular KYC process at an external provider (such as
+ Challenger). If the user chooses to initate a KYC process at an external
+ provider, the SPA must request the respective process to be set-up by the
+ exchange via the ``/kyc-start/`` endpoint.
**Request**:
@@ -458,7 +493,9 @@ New Endpoints
// ID of the requirement, useful to construct the
// ``/kyc-upload/$ID`` or ``/kyc-start/$ID`` endpoint URLs.
- // Present if and only if "form" is not "INFO".
+ // Present if and only if "form" is not "INFO". The
+ // ``$ID`` value may itself contain ``/`` or ``?`` and
+ // basically encode any URL path (and optional arguments).
id?: string;
}
@@ -492,13 +529,15 @@ New Endpoints
The ``/kyc-upload/$ID`` POST endpoint allows the SPA to upload
client-provided evidence. The ``$ID`` will be provided as part of the
- ``/kyc-info`` body. This is for checks of type ``FORM``.
+ ``/kyc-info`` body. This is for checks of type ``FORM``. In practice,
+ ``$ID`` will encode both the ``$TARGET_TOKEN`` and the index of the selected
+ measure (but this should be irrelevant for the client).
**Request**:
Basically oriented along the possible formats of a HTTP form being
- POSTed. Details will depend on the form. The server will try to
- decode the uploaded body from whatever format it is provided in.
+ POSTed. Details will depend on the form. The server will try to decode the
+ uploaded body from whatever format it is provided in.
**Response**:
@@ -514,11 +553,12 @@ New Endpoints
.. http:post:: /kyc-start/$ID
- The ``/kyc-start/$ID`` POST endpoint allows the SPA to set up a new
- external KYC process. It will return the URL that the client must GET
- to begin the KYC process. The SPA should probably open this URL in a new
- window or tab. The ``$ID`` will be provided as part of the ``/kyc-info``
- body.
+ The ``/kyc-start/$ID`` POST endpoint allows the SPA to set up a new external
+ KYC process. It will return the URL that the client must GET to begin the
+ KYC process. The SPA should probably open this URL in a new window or tab.
+ The ``$ID`` will be provided as part of the ``/kyc-info`` body. In
+ practice, ``$ID`` will encode both the ``$TARGET_TOKEN`` and the index of
+ the selected measure (but this should be irrelevant for the client).
**Request**:
@@ -548,21 +588,72 @@ New Endpoints
in the future (since **vATTEST**).
-.. http:get:: /kyc-proof/$H_PAYTO/$PROVIDER_SECTION
+.. http:get:: /kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO
Upon completion of the process at the external KYC provider, the provider
- must trigger a GET request to a new ``/kyc-proof/$H_PAYTO/$PROVIDER_SECTION``
- endpoint. This may be done either by redirecting the browser of the user to
- that endpoint. Once this endpoint is triggered, the exchange will pass the
- received arguments to the respective logic plugin. The logic plugin will then
- (asynchronously) update the KYC status of the user. The logic plugin should
- return a human-readable HTML page with the KYC result to the user.
+ must redirect the client (browser) to trigger a GET request to a new
+ ``/kyc-proof/$H_PAYTO/$PROVIDER_SECTION`` endpoint. Once this endpoint is
+ triggered, the exchange will pass the received arguments to the respective
+ logic plugin. The logic plugin will then (asynchronously) update the KYC
+ status of the user. The logic plugin should return a human-readable HTML
+ page with the KYC result to the user. This endpoint deliberately does
+ not use the ``$TARGET_TOKEN`` as the provider should not learn that token.
+
+ This endpoint is thus accessed from the user's browser at the *end* of a KYC
+ process, possibly providing the exchange with additional credentials to
+ obtain the results of the KYC process. Specifically, the URL arguments
+ should provide information to the exchange that allows it to verify that the
+ user has completed the KYC process. The details depend on the logic, which
+ is selected by the "$PROVIDER_SECTION".
+
+ While this is a GET (and thus safe, and idempotent), the operation may
+ actually trigger significant changes in the exchange's state. In
+ particular, it may update the KYC status of a particular payment target.
+
+ **Request:**
+
+ Details on the request depend on the specific KYC logic that was used.
+
+ If the KYC plugin logic is OAuth 2.0, the query parameters are:
+
+ :query code=CODE: OAuth 2.0 code argument.
+ :query state=STATE: OAuth 2.0 state argument with the H_PAYTO.
+
+ .. note::
+
+ Depending on the OAuth variant used, additional
+ query parameters may need to be passed here.
+
+ **Response:**
+
+ Given that the response is returned to a user using a browser and **not** to
+ a Taler wallet, the response format is in human-readable HTML and not in
+ machine-readable JSON.
+
+ :http:statuscode:`302 Found`:
+ The KYC operation succeeded and the
+ payment target is now authorized to transact.
+ The browser is redirected to a human-readable
+ page configured by the exchange operator.
+ :http:statuscode:`401 Unauthorized`:
+ The provided authorization token is invalid.
+ :http:statuscode:`404 Not found`:
+ The payment target is unknown.
+ :http:statuscode:`502 Bad Gateway`:
+ The exchange received an invalid reply from the
+ legitimization service.
+ :http:statuscode:`504 Gateway Timeout`:
+ The exchange did not receive a reply from the legitimization
+ service within a reasonable time period.
-.. http:get:: /kyc-webhook/*``
-.. http:post:: /kyc-webhook/*``
+
+.. http:get:: /kyc-webhook/$PROVIDER_SECTION/*
+.. http:post:: /kyc-webhook/$PROVIDER_SECTION/*
+.. http:get:: /kyc-webhook/$LOGIC/*
+.. http:post:: /kyc-webhook/$LOGIC/*
Alternatively, the KYC confirmation may be triggered by a ``/kyc-webhook``
- request. As KYC providers do not necessarily support passing detailed
+ request. As KYC **providers** do not necessarily support passing detailed
information in the URL arguments, the ``/kyc-webhook`` only needs to specify
either the ``PROVIDER_SECTION`` *or* the ``LOGIC`` (the name of the plugin
implementing the KYC API). The API-specific webhook logic must then figure
@@ -571,6 +662,18 @@ New Endpoints
In contrast to ``kyc-proof``, the response does NOT go to the end-users'
browser and should thus only indicate success or failure.
+ **Request:**
+
+ Details on the request depend on the specific KYC logic that was used.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The operation succeeded.
+ :http:statuscode:`404 Not found`:
+ The specified logic is unknown.
+
+
.. http:post:: /kyc-wallet``
The ``/wallet-kyc`` POST endpoint allows a wallet to notify an exchange if
@@ -584,12 +687,11 @@ New Endpoints
cryptographically enforce this), such modifications are a terms of service
violation which may have legal consequences for the user.
- Setup KYC identification for a wallet. Returns the KYC UUID.
- This endpoint is used by compliant Taler wallets when they
- are about to hit the balance threshold and thus need to have
- the customer provide their personal details to the exchange.
- The wallet is identified by its long-lived reserve public key
- (which is used for P2P payments, not for withdrawals).
+ Setup KYC identification for a wallet. Returns the KYC UUID. This endpoint
+ is used by compliant Taler wallets when they are about to hit the balance
+ threshold and thus need to have the customer provide their personal details
+ to the exchange. The wallet is identified by its long-lived reserve public
+ key (which is used for P2P payments, not for withdrawals).
**Request:**
@@ -598,10 +700,9 @@ New Endpoints
**Response:**
:http:statuscode:`204 No Content`:
- KYC is disabled at this exchange, or the balance
- is below the threshold that requires KYC, or this
- wallet already satisfied the KYC check for the
- given balance.
+ KYC is disabled at this exchange, or the balance is below the
+ threshold that requires KYC, or this wallet already satisfied
+ the KYC check for the given balance.
:http:statuscode:`403 Forbidden`:
The provided signature is invalid.
This response comes with a standard `ErrorDetail` response.
@@ -703,11 +804,9 @@ New Endpoints
// Description of what the AML program does.
description: string;
- // List of required field names in the context
- // to run this AML program.
- // SPA must check that the AML staff is providing
- // adequate CONTEXT when defining a measure using
- // this AML program.
+ // List of required field names in the context to run this
+ // AML program. SPA must check that the AML staff is providing
+ // adequate CONTEXT when defining a measure using this program.
context: string[];
// List of required attribute names in the
@@ -1237,8 +1336,10 @@ on GET ``/deposits/`` with the respective legitimization requirement row.
CREATE TABLE wire_targets
(wire_target_serial_id BIGSERIAL UNIQUE
,h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=64),
+ ,target_token BYTEA UNIQUE CHECK (LENGTH(target_token)=32)
+ ,target_pub BYTEA CHECK (LENGTH(target_pub)=32)
,payto_uri STRING NOT NULL
- ,PRIMARY KEY (h_payto)
+ ,PRIMARY KEY (h_payto,target_pub)
)
PARTITION BY HASH (h_payto);
@@ -1248,25 +1349,31 @@ on GET ``/deposits/`` with the respective legitimization requirement row.
IS 'Can be a regular bank account, or also be a URI identifying a reserve-account (for P2P payments)';
COMMENT ON COLUMN wire_targets.h_payto
IS 'Unsalted hash of payto_uri';
+ COMMENT ON COLUMN wire_targets.target_token
+ IS 'high-entropy random value that uniquely identifies the wire target and is used as a token to authorize access to the KYC process (without requiring a signature by target_priv); NULL if KYC is not allowed for the account (legacy)';
+ COMMENT ON COLUMN wire_targets.target_pub
+ IS 'Public key (reserve_pub or merchant_pub) associated with the account; NULL if KYC is not allowed for the account (legacy)';
CREATE TABLE IF NOT EXISTS legitimization_measures
(legitimization_measure_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
- ,h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=32)
- REFERENCES wire_targets (h_payto)
+ ,target_token BYTEA NOT NULL UNIQUE CHECK (LENGTH(target_token)=32)
+ REFERENCES wire_targets (target_token)
,start_time INT8 NOT NULL
,jmeasures VARCHAR[] NOT NULL
,is_finished BOOL NOT NULL DEFAULT(FALSE)
)
PARTITION BY HASH (h_payto);
+ COMMENT ON COLUMN legitimization_requirements.target_token
+ IS 'Used to uniquely identify the account and as a symmetric access control mechanism for the SPA';
COMMENT ON COLUMN legitimization_requirements.start_time
IS 'Time when the measure was triggered (by decision or rule)';
- COMMENT ON COLUMN legitimization_requirements.is_finished
- IS 'Set to TRUE if this set of measures was processed; used to avoid indexing measures that are done';
COMMENT ON COLUMN legitimization_requirements.jmeasures
IS 'JSON object of type LegitimizationMeasures with KYC/AML measures for the account encoded';
+ COMMENT ON COLUMN legitimization_requirements.is_finished
+ IS 'Set to TRUE if this set of measures was processed; used to avoid indexing measures that are done';
- CREATE INDEX ON legitimization_measures (h_payto)
+ CREATE INDEX ON legitimization_measures (target_token)
WHERE NOT finished;
CREATE TABLE legitimization_outcomes