taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

api-challenger.rst (20277B)


      1 ..
      2   This file is part of GNU TALER.
      3   Copyright (C) 2023, 2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 2.1, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 
     16   @author Christian Grothoff
     17 
     18 .. _challenger-api:
     19 
     20 ======================
     21 Challenger Service API
     22 ======================
     23 
     24 The challenger service validates that a user is able to receive challenges at
     25 an address (such as e-mail or SMS) and allows an OAuth 2.0 client to obtain
     26 access to these validated addresses.
     27 
     28 The high-level flow is that an OAuth 2.0 client is first registered with the
     29 challenger service (via command-line). Using the command-line tool will print
     30 the resulting client ID to the console.
     31 
     32 .. note::
     33 
     34   Right now, registration of a unique redirection URI is *mandatory* for
     35   each client. If multiple redirection URIs are needed, it is suggested to
     36   just register additional clients.  (While OAuth 2.0 would support not
     37   registering fixed redirection URIs with a client, this is not recommended
     38   as it would create an open redirector.)
     39 
     40 Once a client is registered, that client can use the challenger service when
     41 it needs a user to prove that the user is able to receive messages at a
     42 particular address.  However, asking a user to prove access to a particular
     43 address can be expensive as it may involve sending an SMS or even postal mail
     44 depending on the type of address.  Thus, challenger does not allow a user
     45 agent to begin an address validation process without prior approval by a
     46 registered client.  Thus, the process begins with a ``/setup/$CLIENT_ID`` request where a
     47 client requests challenger to begin an address validation request.  The
     48 ``/setup/$CLIENT_ID`` response contains a ``nonce`` which is then used to construct the
     49 URL of the endpoint to which the client must redirect the user-agent to begin
     50 the address validation and authorization process.
     51 
     52 The client then redirects the user-agent to the ``/authorize/$NONCE`` endpoint
     53 of the challenger service, adding its ``state``, ``client_id`` and
     54 ``redirect_uri`` as query parameters.  The ``redirect_uri`` must match the
     55 redirect URI registered with the client. From this endpoint, the challenger
     56 service will return a Web page asking the user to provide its address.
     57 
     58 .. note::
     59 
     60   Challenger is a bit unusual in that the ``$NONCE`` in the endpoint URL
     61   makes the authorization endpoint URL (deliberately) unpredictable, while
     62   for many other OAuth 2.0 APIs this endpoint is static. However, this is
     63   compliant with OAuth 2.0 as determining the authorization URL is left out
     64   of the scope of the standard.
     65 
     66 When the user has filled in the form with their address, it will be submitted
     67 to the ``/challenge/$NONCE`` endpoint and the challenger service will send a
     68 challenge to the user's address and generate an HTML form asking the user to
     69 enter the received challenge value.
     70 
     71 The user can then enter the answer to the challenge which is then submitted to
     72 the ``/solve/$NONCE`` endpoint.  If the answer is correct, the user agent will
     73 be redirected to the client redirect URI that was specified by the OAuth 2.0
     74 client upon ``/authorize``, together with an authorization grant encoded in
     75 the redirection URI.
     76 
     77 Given this authorization grant, the OAuth 2.0 client can then use the
     78 ``/token`` endpoint to obtain an access token which will grant it access to
     79 the resource.
     80 
     81 Using the ``/info`` endpoint the client can then finally obtain the (now)
     82 verified address of the user.
     83 
     84 .. contents:: Table of Contents
     85   :local:
     86 
     87 .. include:: tos.rst
     88 
     89 .. _challenger-config:
     90 
     91 -----------------------
     92 Receiving Configuration
     93 -----------------------
     94 
     95 .. http:get:: /config
     96 
     97   Obtain the key configuration settings of the storage service.
     98   This specification corresponds to ``current`` protocol being version **v6**.
     99 
    100   **Response:**
    101 
    102   Returns a `ChallengerTermsOfServiceResponse`.
    103 
    104   .. ts:def:: ChallengerTermsOfServiceResponse
    105 
    106     interface ChallengerTermsOfServiceResponse {
    107       // Name of the service
    108       name: "challenger";
    109 
    110       // libtool-style representation of the Challenger protocol version, see
    111       // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
    112       // The format is "current:revision:age".
    113       version: string;
    114 
    115       // URN of the implementation (needed to interpret 'revision' in version).
    116       // @since v0, may become mandatory in the future.
    117       implementation?: string;
    118 
    119       // @since **v2**.
    120       // Object; map of keys (names of the fields of the address
    121       // to be entered by the user) to objects with a "regex" (string)
    122       // containing an extended Posix regular expression for allowed
    123       // address field values, and a "hint"/"hint_i18n" giving a
    124       // human-readable explanation to display if the value entered
    125       // by the user does not match the regex. Keys that are not mapped
    126       // to such an object have no restriction on the value provided by
    127       // the user.  See "ADDRESS_RESTRICTIONS" in the challenger configuration.
    128       restrictions: Object;
    129 
    130       // @since **v6**
    131       // Defines the set of fields asked to the user.
    132       // The field names are registered via GANA at
    133       // https://git.taler.net/gana.git/tree/gnu-taler-form-attributes
    134       // email: CONTACT_EMAIL
    135       // phone: CONTACT_PHONE
    136       // postal: CONTACT_NAME, ADDRESS_LINES, ADDRESS_COUNTRY
    137       // postal-ch: CONTACT_NAME, ADDRESS_LINES
    138       address_type: "email" | "phone" | "postal" | "postal-ch";
    139 
    140       // Hint to show in the address bar for the user as an example for
    141       // the format of the address.
    142       address_hint: string;
    143     }
    144 
    145 .. _challenger-setup:
    146 
    147 -----
    148 Setup
    149 -----
    150 
    151 .. http:post:: /setup/$CLIENT_ID
    152 
    153   This endpoint is used by the client to authorize the execution of an address
    154   validation on its behalf.  An ``Authorization`` header (for now always using
    155   a ``Bearer`` token) should be included to provide the client's credentials
    156   to authorize access to the challenger service.  This token must match the
    157   ``client_secret`` from the registration of the client with the challenger
    158   service (which will also be used in the later ``/token`` request).
    159 
    160   **Request:**
    161 
    162   The body can be an address in JSON encoding to pre-initialize the address to
    163   be used by challenger for this process. If the body is absent, the user will
    164   have to enter the full address details.  The specific address format depends
    165   on the address type.  However, `ChallengeSetupRequest` defines the shared
    166   ``read_only`` bit that has a special meaning independent of the address type:
    167   it informs Challenger that the address should not be editable.
    168 
    169   Passing an address in the ``/setup`` body is supported @since protocol **v4**.
    170 
    171   **Response:**
    172 
    173   :http:statuscode:`200 OK`:
    174     Response is a `ChallengeSetupResponse`.
    175   :http:statuscode:`404 Not found`:
    176     The backup service is unaware of a matching client.
    177     or the credentials of the client are invalid.
    178 
    179   **Details::**
    180 
    181   .. ts:def:: ChallengeSetupRequest
    182 
    183     interface ChallengeSetupRequest {
    184       // If true, the given address should not be edited.
    185       // Defaults to 'false' if not specified.
    186       read_only?: boolean;
    187 
    188       // Optional, additional fields to pre-populate
    189       // the address to be validated.
    190       // The fields depend on the challenger type.
    191       [x: string]: any;
    192     }
    193 
    194 
    195   .. ts:def:: ChallengeSetupResponse
    196 
    197     interface ChallengeSetupResponse {
    198       // Nonce to use when constructing ``/authorize`` endpoint.
    199       nonce: string;
    200     }
    201 
    202 
    203 .. _challenger-login:
    204 
    205 -----
    206 Login
    207 -----
    208 
    209 .. http:get:: /authorize/$NONCE
    210 .. http:post:: /authorize/$NONCE
    211 
    212   This is the "authorization" endpoint of the OAuth 2.0 protocol.  This
    213   endpoint is used by the user-agent. It will return a form to enter the
    214   address.
    215 
    216   The NONCE is a unique value identifying the challenge, should be shown to
    217   the user so that they can recognize it when they receive the TAN code.
    218 
    219 
    220   **Request:**
    221 
    222   :query response_type: Must be ``code``
    223   :query client_id: Identifier of the client.
    224   :query redirect_uri: URI-encoded redirection URI to use upon authorization.
    225   :query state: Arbitrary client state to associate with the request.
    226   :query scope: Not supported, any value is accepted.
    227   :query code_challenge: A string to enhance security using PKCE (available since **v3**).
    228   :query code_challenge_method: The method used for the code_challenge. Options are S256 (SHA-256) or plain (available since **v3**).
    229 
    230   **Response:**
    231 
    232   :http:statuscode:`200 OK`:
    233     The the response is
    234     a `ChallengeStatus`. Since protocol **v1**.
    235   :http:statuscode:`400 Bad Request`:
    236     The request does not follow the spec.
    237     The response will include error
    238     code, hint and detail. Since protocol **v1**.
    239   :http:statuscode:`404 Not found`:
    240     The service is unaware of a matching challenge.
    241     The response will include error
    242     code, hint and detail. Since protocol **v1**.
    243   :http:statuscode:`406 Not Acceptable`:
    244     The client ask for "text/html" and the backend installation does
    245     not include the required HTML templates.
    246   :http:statuscode:`500 Internal Server Error`:
    247     Server is not able to respond due to internal problems.
    248     The response will include error
    249     code, hint and detail. Since protocol **v1**.
    250 
    251   .. ts:def:: ChallengeStatus
    252 
    253     interface ChallengeStatus {
    254       // @deprecated since **v2**, use /config
    255       restrictions?: Object;
    256 
    257       // indicates if the given address cannot be changed anymore, the
    258       // form should be read-only if set to true.
    259       fix_address: boolean;
    260 
    261       // form values from the previous submission if available, details depend
    262       // on the ``ADDRESS_TYPE``, should be used to pre-populate the form
    263       last_address?: Object;
    264 
    265       // is the challenge already solved?
    266       solved: boolean;
    267 
    268       // number of times the address can still be changed, may or may not be
    269       // shown to the user
    270       changes_left: Integer;
    271 
    272       // when we would re-transmit the challenge the next
    273       // time (at the earliest) if requested by the user
    274       // only present if challenge already created
    275       // @since **v2**
    276       retransmission_time: Timestamp;
    277 
    278       // how many times might the PIN still be retransmitted
    279       // only present if challenge already created
    280       // @since **v2**
    281       pin_transmissions_left?: Integer;
    282 
    283       // how many times might the user still try entering the PIN code
    284       // only present if challenge already created
    285       // @since **v2**
    286       auth_attempts_left?: Integer;
    287     }
    288 
    289 
    290 .. _challenger-challenge:
    291 
    292 ---------
    293 Challenge
    294 ---------
    295 
    296 .. http:post:: /challenge/$NONCE
    297 
    298   This endpoint is used by the user-agent to submit the address to which a
    299   challenge should be sent by the challenger service.
    300 
    301   **Request:**
    302 
    303   Body should use the mime-type "application/x-www-form-urlencoded".
    304   The posted form data must contain an object that follow the restrictions
    305   defined in :ref:`config <challenger-config>`.
    306 
    307   **Response:**
    308 
    309   :http:statuscode:`200 OK`:
    310     The response is `ChallengeResponse`. Since protocol **v2**.
    311   :http:statuscode:`400 Bad Request`:
    312     The request does not follow the spec.
    313     The response will include error
    314     code, hint and detail. Since protocol **v1**.
    315   :http:statuscode:`404 Not Found`:
    316     The service is unaware of a matching challenge.
    317     The response will include error
    318     code, hint and detail. Since protocol **v1**.
    319   :http:statuscode:`406 Not Acceptable`:
    320     The client ask for "text/html" and the backend installation does
    321     not include the required HTML templates.
    322   :http:statuscode:`429 Too Many Requests`:
    323     There have been too many attempts to request challenge
    324     transmissions for this $NONCE. The user-agent should
    325     wait and (eventually) request a fresh nonce to be set
    326     up by the client.
    327     The response will include error
    328     code, hint and detail. Since protocol **v2**.
    329   :http:statuscode:`500 Internal Server Error`:
    330     Server is not able to respond due to internal problems.
    331     The response will include error
    332     code, hint and detail. Since protocol **v1**.
    333 
    334   .. ts:def:: ChallengeResponse
    335 
    336     // Union discriminated by the "type" field.
    337     type ChallengeResponse = ChallengeRedirect | ChallengeCreateResponse
    338 
    339   .. ts:def:: ChallengeRedirect
    340 
    341     // @since **v2**
    342     interface ChallengeRedirect {
    343       // Union discriminator field.
    344       type: "completed";
    345 
    346       // challenge is completed, use should redirect here
    347       redirect_url: string;
    348     }
    349 
    350   .. ts:def:: ChallengeCreateResponse
    351 
    352    interface ChallengeCreateResponse {
    353       // Union discriminator field.
    354       type: "created"
    355 
    356       // how many more attempts are allowed, might be shown to the user,
    357       // highlighting might be appropriate for low values such as 1 or 2 (the
    358       // form will never be used if the value is zero)
    359       attempts_left: Integer;
    360 
    361       // the address that is being validated, might be shown or not
    362       address: Object;
    363 
    364       // true if we just retransmitted the challenge, false if we sent a
    365       // challenge recently and thus refused to transmit it again this time;
    366       // might make a useful hint to the user
    367       transmitted: boolean;
    368 
    369       // @deprecated in **v2**, use retransmission_time
    370       next_tx_time?: string;
    371 
    372       // when we would re-transmit the challenge the next
    373       // time (at the earliest) if requested by the user
    374       // @since **v2**
    375       retransmission_time: Timestamp;
    376     }
    377 
    378 
    379 .. _challenger-solve:
    380 
    381 -----
    382 Solve
    383 -----
    384 
    385 .. http:post:: /solve/$NONCE
    386 
    387   Used by the user-agent to submit an answer to the challenge.  If the answer
    388   is correct, the user will be redirected to the client's redirect URI,
    389   otherwise the user may be given another chance to complete the process.
    390 
    391   **Request:**
    392 
    393   Body should use the mime-type "application/x-www-form-urlencoded".
    394   The posted form data must contain a "pin" field.
    395 
    396   **Response:**
    397 
    398   :http:statuscode:`200 OK`:
    399     If the request ask for application/json the response is
    400     a `ChallengeSolveResponse`. Since protocol **v2**.
    401   :http:statuscode:`302 Found`:
    402     Only possible if request didn't ask for application/json. Since protocol **v2**.
    403     The user is redirected to the redirect URI of the client to pass the
    404     grant to the client.  The target will be the redirect URI specified
    405     by the client (during registration and again upon ``/authorize``),
    406     plus a ``code`` argument with the authorization code, and the
    407     ``state`` argument from the ``/authorize`` endpoint.
    408   :http:statuscode:`400 Bad Request`:
    409     The request does not follow the spec.
    410     The response will include error
    411     code, hint and detail. Since protocol **v1**.
    412   :http:statuscode:`403 Forbidden`:
    413     The response is `InvalidPinResponse`. Since protocol **v1**.
    414   :http:statuscode:`404 Not found`:
    415     The service is unaware of a matching challenge.
    416     The response will include error
    417     code, hint and detail. Since protocol **v1**.
    418   :http:statuscode:`429 Too Many Requests`:
    419     There have been too many attempts to solve the challenge
    420     for this address (and $NONCE). The user-agent should
    421     either try a different address (or wait and (eventually)
    422     request a fresh nonce to be set up by the client).
    423     The response will include error
    424     code, hint and detail. Since protocol **v2**.
    425   :http:statuscode:`500 Internal Server Error`:
    426     Server is not able to respond due to internal problems.
    427     The response will include error
    428     code, hint and detail. Since protocol **v1**.
    429 
    430   .. ts:def:: ChallengeSolveResponse
    431 
    432     // Union discriminated by the "type" field.
    433     type ChallengeSolveResponse = ChallengeRedirect | InvalidPinResponse;
    434 
    435   .. ts:def:: InvalidPinResponse
    436 
    437     interface InvalidPinResponse {
    438       // Union discriminator field.
    439       type: "pending";
    440 
    441       // numeric Taler error code, should be shown to indicate the error
    442       // compactly for reporting to developers
    443       code: Integer;
    444 
    445       // human-readable Taler error code, should be shown for the user to
    446       // understand the error
    447       hint: string;
    448 
    449       // how many times is the user still allowed to change the address;
    450       // if 0, the user should not be shown a link to jump to the
    451       // address entry form
    452       addresses_left: Integer;
    453 
    454       // how many times might the PIN still be retransmitted
    455       pin_transmissions_left: Integer;
    456 
    457       // how many times might the user still try entering the PIN code
    458       auth_attempts_left: Integer;
    459 
    460       // if true, the PIN was not even evaluated as the user previously
    461       // exhausted the number of attempts
    462       exhausted: boolean;
    463 
    464       // if true, the PIN was not even evaluated as no challenge was ever
    465       // issued (the user must have skipped the step of providing their
    466       // address first!)
    467       no_challenge: boolean;
    468     }
    469 
    470 .. _challenger-auth:
    471 
    472 ----
    473 Auth
    474 ----
    475 
    476 .. http:post:: /token
    477 
    478   This is the token endpoint of the OAuth 2.0 specification.
    479   This endpoint is used by the client to provide its authorization code,
    480   demonstrating that it has the right to learn a particular user's validated
    481   address.  In return, the challenger service returns the access token.
    482   Renewal is not supported.
    483 
    484   **Request:**
    485 
    486   The request must include an ``application/www-form-urlencoded`` body
    487   specifying the ``client_id``, ``redirect_uri``, ``client_secret``, ``code``
    488   and ``grant_type``.  The ``grant_type`` must be set to
    489   ``authorization_code``.  The ``redirect_uri`` must match the URI from
    490   ``/authorize``. The ``code`` must be the authorization code that ``/solve``
    491   returned to the user.  The ``client_id`` and ``client_secret`` must match
    492   the usual client credentials. Since protocol **v3**, ``code_verifier`` can also be included.
    493 
    494   **Response:**
    495 
    496   Error responses follow RFC 6749, section 5.2 with an "error" field in JSON,
    497   as well as also returning GNU Taler style error messages.
    498 
    499   :http:statuscode:`200 OK`:
    500     The body will be a `ChallengerAuthResponse`
    501   :http:statuscode:`401 Unauthorized`:
    502     The ``code_verifier`` is not matching the saved ones. (Since **v3**)
    503   :http:statuscode:`403 Forbidden`:
    504     The credentials of the client are invalid.
    505   :http:statuscode:`404 Not found`:
    506     The service is unaware of a matching login process.
    507 
    508   **Details::**
    509 
    510   .. ts:def:: ChallengerAuthResponse
    511 
    512     interface ChallengerAuthResponse {
    513       // Token used to authenticate access in ``/info``.
    514       access_token: string;
    515 
    516       // Type of the access token.
    517       token_type: "Bearer";
    518 
    519       // Amount of time that an access token is valid (in seconds).
    520       expires_in: Integer;
    521 
    522     }
    523 
    524 
    525 .. _challenger-info:
    526 
    527 ----
    528 Info
    529 ----
    530 
    531 .. http:get:: /info
    532 
    533   This userinfo endpoint of the OAuth 2.0 specification.
    534   This endpoint is used by the client to obtain the user's validated address.
    535 
    536   **Request:**
    537 
    538   Must include the token returned to the client from the ``/token`` endpoint
    539   as a ``Bearer`` token in an ``Authorization`` header.
    540 
    541   **Response:**
    542 
    543   :http:statuscode:`200 OK`:
    544     The body contains the address as a `ChallengerInfoResponse`.
    545   :http:statuscode:`403 Forbidden`:
    546     The bearer token is missing or invalid (malformed).
    547   :http:statuscode:`404 Not found`:
    548     The bearer token is invalid (includes unknown or expired).
    549 
    550   **Details::**
    551 
    552   .. ts:def:: ChallengerInfoResponse
    553 
    554     interface ChallengerInfoResponse {
    555 
    556       // Unique ID of the record within Challenger
    557       // (identifies the rowid of the token).
    558       id: Integer;
    559 
    560       // Address that was validated.
    561       // Key-value pairs, details depend on the
    562       // address_type.
    563       address: Object;
    564 
    565      // Type of the address.
    566       address_type: string;
    567 
    568       // How long do we consider the address to be
    569       // valid for this user.
    570       expires: Timestamp;
    571 
    572     }