summaryrefslogtreecommitdiff
path: root/core/api-challenger.rst
diff options
context:
space:
mode:
Diffstat (limited to 'core/api-challenger.rst')
-rw-r--r--core/api-challenger.rst548
1 files changed, 548 insertions, 0 deletions
diff --git a/core/api-challenger.rst b/core/api-challenger.rst
new file mode 100644
index 00000000..afb3096d
--- /dev/null
+++ b/core/api-challenger.rst
@@ -0,0 +1,548 @@
+..
+ This file is part of GNU TALER.
+ Copyright (C) 2023, 2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Christian Grothoff
+
+.. _challenger-api:
+
+======================
+Challenger Service API
+======================
+
+The challenger service validates that a user is able to receive challenges at
+an address (such as e-mail or SMS) and allows an OAuth 2.0 client to obtain
+access to these validated addresses.
+
+The high-level flow is that an OAuth 2.0 client is first registered with the
+challenger service (via command-line). Using the command-line tool will print
+the resulting client ID to the console.
+
+.. note::
+
+ The current service mandates that redirection URIs
+ start with "http://" or "https://". See issue #7838
+ for what should be done to lift this restriction.
+
+.. note::
+
+ Right now, registration of a unique redirection URI is *mandatory* for
+ each client. If multiple redirection URIs are needed, it is suggested to
+ just register additional clients. (While OAuth 2.0 would support not
+ registering fixed redirection URIs with a client, this is not recommended
+ as it would create an open redirector.)
+
+Once a client is registered, that client can use the challenger service when
+it needs a user to prove that the user is able to receive messages at a
+particular address. However, asking a user to prove access to a particular
+address can be expensive as it may involve sending an SMS or even postal mail
+depending on the type of address. Thus, challenger does not allow a user
+agent to begin an address validation process without prior approval by a
+registered client. Thus, the process begins with a ``/setup/$CLIENT_ID`` request where a
+client requests challenger to begin an address validation request. The
+``/setup/$CLIENT_ID`` response contains a ``nonce`` which is then used to construct the
+URL of the endpoint to which the client must redirect the user-agent to begin
+the address validation and authorization process.
+
+The client then redirects the user-agent to the ``/authorize/$NONCE`` endpoint
+of the challenger service, adding its ``state``, ``client_id`` and
+``redirect_uri`` as query parameters. The ``redirect_uri`` must match the
+redirect URI registered with the client. From this endpoint, the challenger
+service will return a Web page asking the user to provide its address.
+
+.. note::
+
+ Challenger is a bit unusual in that the ``$NONCE`` in the endpoint URL
+ makes the authorization endpoint URL (deliberately) unpredictable, while
+ for many other OAuth 2.0 APIs this endpoint is static. However, this is
+ compliant with OAuth 2.0 as determining the authorization URL is left out
+ of the scope of the standard.
+
+When the user has filled in the form with their address, it will be submitted
+to the ``/challenge/$NONCE`` endpoint and the challenger service will send a
+challenge to the user's address and generate an HTML form asking the user to
+enter the received challenge value.
+
+The user can then enter the answer to the challenge which is then submitted to
+the ``/solve/$NONCE`` endpoint. If the answer is correct, the user agent will
+be redirected to the client redirect URI that was specified by the OAuth 2.0
+client upon ``/authorize``, together with an authorization grant encoded in
+the redirection URI.
+
+Given this authorization grant, the OAuth 2.0 client can then use the
+``/token`` endpoint to obtain an access token which will grant it access to
+the resource.
+
+Using the ``/info`` endpoint the client can then finally obtain the (now)
+verified address of the user.
+
+.. contents:: Table of Contents
+ :local:
+
+.. include:: tos.rst
+
+-----------------------
+Receiving Configuration
+-----------------------
+
+.. http:get:: /config
+
+ Obtain the key configuration settings of the storage service.
+ This specification corresponds to ``current`` protocol being version **1**.
+
+ **Response:**
+
+ Returns a `ChallengerTermsOfServiceResponse`.
+
+ .. ts:def:: ChallengerTermsOfServiceResponse
+
+ interface ChallengerTermsOfServiceResponse {
+ // Name of the service
+ name: "challenger";
+
+ // libtool-style representation of the Challenger protocol version, see
+ // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ // The format is "current:revision:age".
+ version: string;
+
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since v0, may become mandatory in the future.
+ implementation?: string;
+
+ // @since v2.
+ // Object; map of keys (names of the fields of the address
+ // to be entered by the user) to objects with a "regex" (string)
+ // containing an extended Posix regular expression for allowed
+ // address field values, and a "hint"/"hint_i18n" giving a
+ // human-readable explanation to display if the value entered
+ // by the user does not match the regex. Keys that are not mapped
+ // to such an object have no restriction on the value provided by
+ // the user. See "ADDRESS_RESTRICTIONS" in the challenger configuration.
+ restrictions: Object;
+
+ // @since v2.
+ address_type: "email" | "phone";
+ }
+
+.. _challenger-setup:
+
+-----
+Setup
+-----
+
+.. http:post:: /setup/$CLIENT_ID
+
+ This endpoint is used by the client to authorize the execution of an address
+ validation on its behalf. An ``Authorization`` header (for now always using
+ a ``Bearer`` token) should be included to provide the client's credentials
+ to authorize access to the challenger service. This token must match the
+ ``client_secret`` from the registration of the client with the challenger
+ service (which will also be used in the later ``/token`` request).
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ Response is a `ChallengeSetupResponse`.
+ :http:statuscode:`404 Not found`:
+ The backup service is unaware of a matching client.
+ or the credentials of the client are invalid.
+
+ **Details::**
+
+ .. ts:def:: ChallengeSetupResponse
+
+ interface ChallengeSetupResponse {
+ // Nonce to use when constructing ``/authorize`` endpoint.
+ nonce: string;
+ }
+
+
+.. _challenger-login:
+
+-----
+Login
+-----
+
+.. http:get:: /authorize/$NONCE
+.. http:post:: /authorize/$NONCE
+
+ This is the "authorization" endpoint of the OAuth 2.0 protocol. This
+ endpoint is used by the user-agent. It will return a form to enter the
+ address.
+
+ The NONCE is a unique value identifying the challenge, should be shown to
+ the user so that they can recognize it when they receive the TAN code.
+
+ This endpoint typically also supports requests with the "Accept" header
+ requesting "text/html". In this case, an HTML response using the template
+ :ref:`enter-$ADDRESS_TYPE-form <challenger_enter-address_type-form>` is
+ returned. If the backend installation does not include the required HTML
+ templates, a 406 status code is returned.
+
+
+ **Request:**
+
+ :query response_type: Must be ``code``
+ :query client_id: Identifier of the client.
+ :query redirect_uri: URI-encoded redirection URI to use upon authorization.
+ :query state: Arbitrary client state to associate with the request.
+ :query scope: Not supported, any value is accepted.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ If the request ask for application/json then the response is
+ a `ChallengeStatus`. Since protocol **v1**.
+ Otherwise, the body contains a form to be submitted by the user-agent
+ using the template :ref:`enter-$ADDRESS_TYPE-form <challenger_enter-address_type-form>`.
+ The form will ask the user to specify their address.
+ :http:statuscode:`400 Bad Request`:
+ The request does not follow the spec.
+ If the request ask for application/json the response will include error
+ code, hint and detail. Since protocol **v1**.
+ Otherwise, the body contains information using the template :ref:`invalid-request <challenger_invalid-request>`.
+ :http:statuscode:`404 Not found`:
+ The service is unaware of a matching challenge.
+ If the request ask for application/json the response will include error
+ code, hint and detail. Since protocol **v1**.
+ Otherwise, the body contains information using the template :ref:`validation-unknown <challenger_validation-unknown>`.
+ :http:statuscode:`406 Not Acceptable`:
+ The client ask for "text/html" and the backend installation does
+ not include the required HTML templates.
+ :http:statuscode:`500 Internal Server Error`:
+ Server is not able to respond due to internal problems.
+ If the request ask for application/json the response will include error
+ code, hint and detail. Since protocol **v1**.
+ Otherwise, the body contains information using the template :ref:`internal-error <challenger_internal-error>`.
+
+ .. ts:def:: ChallengeStatus
+
+ interface ChallengeStatus {
+ // @deprecated since v2, use /config
+ restrictions?: Object;
+
+ // indicates if the given address cannot be changed anymore, the
+ // form should be read-only if set to true.
+ fix_address: boolean;
+
+ // form values from the previous submission if available, details depend
+ // on the ``ADDRESS_TYPE``, should be used to pre-populate the form
+ last_address?: Object;
+
+ // number of times the address can still be changed, may or may not be
+ // shown to the user
+ changes_left: Integer;
+
+ // when we would re-transmit the challenge the next
+ // time (at the earliest) if requested by the user
+ // only present if challenge already created
+ // @since v2
+ retransmission_time: Timestamp;
+
+ // how many times might the PIN still be retransmitted
+
+ // how many times might the PIN still be retransmitted
+ // only present if challenge already created
+ // @since v2
+ pin_transmissions_left?: Integer;
+
+ // how many times might the user still try entering the PIN code
+ // only present if challenge already created
+ // @since v2
+ auth_attempts_left?: Integer;
+ }
+
+
+.. _challenger-challenge:
+
+---------
+Challenge
+---------
+
+.. http:post:: /challenge/$NONCE
+
+ This endpoint is used by the user-agent to submit the address to which a
+ challenge should be sent by the challenger service.
+
+ **Request:**
+
+ Body should use the mime-type "application/x-www-form-urlencoded".
+ The posted form data must contain an "address".
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ If the request ask for application/json the response is `ChallengeResponse`. Since protocol **v2**.
+ Otherwise, the body contains a form asking for the answer to
+ the challenge to be entered by the user using the
+ template :ref:`enter-tan-form <challenger_enter-tan-form>`.
+ :http:statuscode:`302 Found`:
+ Only possible if request didn't ask for application/json. Since protocol **v2**.
+ The user is redirected to the redirect URI of the client to pass the
+ grant to the client. The target will be the redirect URI specified
+ by the client (during registration and again upon ``/authorize``),
+ plus a ``code`` argument with the authorization code, and the
+ ``state`` argument from the ``/authorize`` endpoint.
+ :http:statuscode:`400 Bad Request`:
+ The request does not follow the spec.
+ If the request ask for application/json the response will include error
+ code, hint and detail. Since protocol **v1**.
+ Otherwise, the body contains information using the template :ref:`invalid-request <challenger_invalid-request>`.
+ :http:statuscode:`404 Not Found`:
+ The service is unaware of a matching challenge.
+ If the request ask for application/json the response will include error
+ code, hint and detail. Since protocol **v1**.
+ Otherwise, the body contains information using the template :ref:`validation-unknown <challenger_validation-unknown>`.
+ :http:statuscode:`406 Not Acceptable`:
+ The client ask for "text/html" and the backend installation does
+ not include the required HTML templates.
+ :http:statuscode:`429 Too Many Requests`:
+ There have been too many attempts to request challenge
+ transmissions for this $NONCE. The user-agent should
+ wait and (eventually) request a fresh nonce to be set
+ up by the client.
+ :http:statuscode:`500 Internal Server Error`:
+ Server is not able to respond due to internal problems.
+ If the request ask for application/json the response will include error
+ code, hint and detail. Since protocol **v1**.
+ Otherwise, the body contains information using the template :ref:`internal-error <challenger_internal-error>`.
+
+ .. ts:def:: ChallengeResponse
+
+ interface ChallengeResponse = ChallengeRedirect | ChallengeCreateResponse
+
+ .. ts:def:: ChallengeRedirect
+
+ // @since v2
+ interface ChallengeRedirect {
+ // challenge is completed, use should redirect here
+ redirect_url: string;
+ }
+
+ .. ts:def:: ChallengeCreateResponse
+
+ interface ChallengeCreateResponse {
+
+ // how many more attempts are allowed, might be shown to the user,
+ // highlighting might be appropriate for low values such as 1 or 2 (the
+ // form will never be used if the value is zero)
+ attempts_left: Integer;
+
+ // the address that is being validated, might be shown or not
+ address: Object;
+
+ // true if we just retransmitted the challenge, false if we sent a
+ // challenge recently and thus refused to transmit it again this time;
+ // might make a useful hint to the user
+ transmitted: boolean;
+
+ // @deprecated in v2, use retransmission_time
+ next_tx_time?: string;
+
+ // when we would re-transmit the challenge the next
+ // time (at the earliest) if requested by the user
+ // @since v2
+ retransmission_time: Timestamp;
+ }
+
+
+
+.. _challenger-solve:
+
+-----
+Solve
+-----
+
+.. http:post:: /solve/$NONCE
+
+ Used by the user-agent to submit an answer to the challenge. If the answer
+ is correct, the user will be redirected to the client's redirect URI,
+ otherwise the user may be given another chance to complete the process.
+
+ **Request:**
+
+ Depends on the form from ``/challenge``. TBD.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ If the request ask for application/json the response is `ChallengeSolveResponse`. Since protocol **v2**.
+ :http:statuscode:`302 Found`:
+ Only possible if request didn't ask for application/json. Since protocol **v2**.
+ The user is redirected to the redirect URI of the client to pass the
+ grant to the client. The target will be the redirect URI specified
+ by the client (during registration and again upon ``/authorize``),
+ plus a ``code`` argument with the authorization code, and the
+ ``state`` argument from the ``/authorize`` endpoint.
+ :http:statuscode:`400 Bad Request`:
+ The request does not follow the spec.
+ If the request ask for application/json the response will include error
+ code, hint and detail. Since protocol **v1**.
+ Otherwise, the body contains information using the template :ref:`invalid-request <challenger_invalid-request>`.
+ :http:statuscode:`403 Forbidden`:
+ If the request ask for application/json the response is `InvalidPinResponse`. Since protocol **v1**.
+ Otherwise, the body contains information using the template :ref:`invalid-pin <challenger_invalid-pin>`.
+ :http:statuscode:`404 Not found`:
+ The service is unaware of a matching challenge.
+ If the request ask for application/json the response will include error
+ code, hint and detail. Since protocol **v1**.
+ Otherwise, the body contains information using the template :ref:`validation-unknown <challenger_validation-unknown>`.
+ :http:statuscode:`429 Too Many Requests`:
+ There have been too many attempts to solve the challenge
+ for this address (and $NONCE). The user-agent should
+ either try a different address (or wait and (eventually)
+ request a fresh nonce to be set up by the client).
+ :http:statuscode:`500 Internal Server Error`:
+ Server is not able to respond due to internal problems.
+ If the request ask for application/json the response will include error
+ code, hint and detail. Since protocol **v1**.
+ Otherwise, the body contains information using the template :ref:`internal-error <challenger_internal-error>`.
+
+ .. ts:def:: ChallengeSolveResponse
+
+ type ChallengeSolveResponse = ChallengeRedirect | InvalidPinResponse;
+
+ .. ts:def:: InvalidPinResponse
+
+ interface InvalidPinResponse {
+ // numeric Taler error code, should be shown to indicate the error
+ // compactly for reporting to developers
+ ec: Integer;
+
+ // human-readable Taler error code, should be shown for the user to
+ // understand the error
+ hint: string;
+
+ // how many times is the user still allowed to change the address;
+ // if 0, the user should not be shown a link to jump to the
+ // address entry form
+ addresses_left: Integer;
+
+ // how many times might the PIN still be retransmitted
+ pin_transmissions_left: Integer;
+
+ // how many times might the user still try entering the PIN code
+ auth_attempts_left: Integer;
+
+ // if true, the PIN was not even evaluated as the user previously
+ // exhausted the number of attempts
+ exhausted: boolean;
+
+ // if true, the PIN was not even evaluated as no challenge was ever
+ // issued (the user must have skipped the step of providing their
+ // address first!)
+ no_challenge: boolean;
+ }
+
+.. _challenger-auth:
+
+----
+Auth
+----
+
+.. http:post:: /token
+
+ This is the token endpoint of the OAuth 2.0 specification.
+ This endpoint is used by the client to provide its authorization code,
+ demonstrating that it has the right to learn a particular user's validated
+ address. In return, the challenger service returns the access token.
+ Renewal is not supported.
+
+ **Request:**
+
+ The request must include an ``application/www-form-urlencoded`` body
+ specifying the ``client_id``, ``redirect_uri``, ``client_secret``, ``code``
+ and ``grant_type``. The ``grant_type`` must be set to
+ ``authorization_code``. The ``redirect_uri`` must match the URI from
+ ``/authorize``. The ``code`` must be the authorization code that ``/solve``
+ returned to the user. The ``client_id`` and ``client_secret`` must match
+ the usual client credentials.
+
+ **Response:**
+
+ Error responses follow RFC 6749, section 5.2 with an "error" field in JSON,
+ as well as also returning GNU Taler style error messages.
+
+ :http:statuscode:`200 OK`:
+ The body will be a `ChallengerAuthResponse`
+ :http:statuscode:`403 Forbidden`:
+ The credentials of the client are invalid.
+ :http:statuscode:`404 Not found`:
+ The service is unaware of a matching login process.
+
+ **Details::**
+
+ .. ts:def:: ChallengerAuthResponse
+
+ interface ChallengerAuthResponse {
+ // Token used to authenticate access in ``/info``.
+ access_token: string;
+
+ // Type of the access token.
+ token_type: "Bearer";
+
+ // Amount of time that an access token is valid (in seconds).
+ expires_in: Integer;
+
+ }
+
+
+.. _challenger-info:
+
+----
+Info
+----
+
+.. http:get:: /info
+
+ This userinfo endpoint of the OAuth 2.0 specification.
+ This endpoint is used by the client to obtain the user's validated address.
+
+ **Request:**
+
+ Must include the token returned to the client from the ``/token`` endpoint
+ as a ``Bearer`` token in an ``Authorization`` header.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The body contains the address as a `ChallengerInfoResponse`.
+ :http:statuscode:`403 Forbidden`:
+ The bearer token is missing or invalid (malformed).
+ :http:statuscode:`404 Not found`:
+ The bearer token is invalid (includes unknown or expired).
+
+ **Details::**
+
+ .. ts:def:: ChallengerInfoResponse
+
+ interface ChallengerInfoResponse {
+
+ // Unique ID of the record within Challenger
+ // (identifies the rowid of the token).
+ id: Integer;
+
+ // Address that was validated.
+ // Key-value pairs, details depend on the
+ // address_type.
+ address: Object;
+
+ // Type of the address.
+ address_type: string;
+
+ // How long do we consider the address to be
+ // valid for this user.
+ expires: Timestamp;
+
+ }