taler-docs

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

api-challenger.rst (20584B)


      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 RESTful 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 
     88 ---------------
     89 Version History
     90 ---------------
     91 
     92 The current protocol version is **v6**.
     93 
     94 * The Challenger SPA is currently targeting **v6**.
     95 
     96 **Version history:**
     97 
     98 * ``v6``: add the ``address_type`` field to ``/config``
     99 
    100 **Upcoming versions:**
    101 
    102 * None anticipated.
    103 
    104 **Ideas for future version:**
    105 
    106 * ``vXXX``: marker for features not yet targeted for release
    107 
    108 
    109 .. include:: tos.rst
    110 
    111 .. _challenger-config:
    112 
    113 -----------------------
    114 Receiving Configuration
    115 -----------------------
    116 
    117 .. http:get:: /config
    118 
    119   Obtain the key configuration settings of the storage service.
    120 
    121   **Response:**
    122 
    123   Returns a `ChallengerTermsOfServiceResponse`.
    124 
    125   .. ts:def:: ChallengerTermsOfServiceResponse
    126 
    127     interface ChallengerTermsOfServiceResponse {
    128       // Name of the service
    129       name: "challenger";
    130 
    131       // libtool-style representation of the Challenger protocol version, see
    132       // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
    133       // The format is "current:revision:age".
    134       version: string;
    135 
    136       // URN of the implementation (needed to interpret 'revision' in version).
    137       // @since v0, may become mandatory in the future.
    138       implementation?: string;
    139 
    140       // @since **v2**.
    141       // Object; map of keys (names of the fields of the address
    142       // to be entered by the user) to objects with a "regex" (string)
    143       // containing an extended Posix regular expression for allowed
    144       // address field values, and a "hint"/"hint_i18n" giving a
    145       // human-readable explanation to display if the value entered
    146       // by the user does not match the regex. Keys that are not mapped
    147       // to such an object have no restriction on the value provided by
    148       // the user.  See "ADDRESS_RESTRICTIONS" in the challenger configuration.
    149       restrictions: Object;
    150 
    151       // @since **v6**
    152       // Defines the set of fields asked to the user.
    153       // The field names are registered via GANA at
    154       // https://git.taler.net/gana.git/tree/gnu-taler-form-attributes
    155       // email: CONTACT_EMAIL
    156       // phone: CONTACT_PHONE
    157       // postal: CONTACT_NAME, ADDRESS_LINES, ADDRESS_COUNTRY
    158       // postal-ch: CONTACT_NAME, ADDRESS_LINES
    159       address_type: "email" | "phone" | "postal" | "postal-ch";
    160 
    161       // Hint to show in the address bar for the user as an example for
    162       // the format of the address.
    163       address_hint: string;
    164     }
    165 
    166 .. _challenger-setup:
    167 
    168 -----
    169 Setup
    170 -----
    171 
    172 .. http:post:: /setup/$CLIENT_ID
    173 
    174   This endpoint is used by the client to authorize the execution of an address
    175   validation on its behalf.  An ``Authorization`` header (for now always using
    176   a ``Bearer`` token) should be included to provide the client's credentials
    177   to authorize access to the challenger service.  This token must match the
    178   ``client_secret`` from the registration of the client with the challenger
    179   service (which will also be used in the later ``/token`` request).
    180 
    181   **Request:**
    182 
    183   The body can be an address in JSON encoding to pre-initialize the address to
    184   be used by challenger for this process. If the body is absent, the user will
    185   have to enter the full address details.  The specific address format depends
    186   on the address type.  However, `ChallengeSetupRequest` defines the shared
    187   ``read_only`` bit that has a special meaning independent of the address type:
    188   it informs Challenger that the address should not be editable.
    189 
    190   Passing an address in the ``/setup`` body is supported @since protocol **v4**.
    191 
    192   **Response:**
    193 
    194   :http:statuscode:`200 OK`:
    195     Response is a `ChallengeSetupResponse`.
    196   :http:statuscode:`404 Not found`:
    197     The backup service is unaware of a matching client.
    198     or the credentials of the client are invalid.
    199 
    200   **Details::**
    201 
    202   .. ts:def:: ChallengeSetupRequest
    203 
    204     interface ChallengeSetupRequest {
    205       // If true, the given address should not be edited.
    206       // Defaults to 'false' if not specified.
    207       read_only?: boolean;
    208 
    209       // Optional, additional fields to pre-populate
    210       // the address to be validated.
    211       // The fields depend on the challenger type.
    212       [x: string]: any;
    213     }
    214 
    215 
    216   .. ts:def:: ChallengeSetupResponse
    217 
    218     interface ChallengeSetupResponse {
    219       // Nonce to use when constructing ``/authorize`` endpoint.
    220       nonce: string;
    221     }
    222 
    223 
    224 .. _challenger-login:
    225 
    226 -----
    227 Login
    228 -----
    229 
    230 .. http:get:: /authorize/$NONCE
    231 .. http:post:: /authorize/$NONCE
    232 
    233   This is the "authorization" endpoint of the OAuth 2.0 protocol.  This
    234   endpoint is used by the user-agent. It will return a form to enter the
    235   address.
    236 
    237   The NONCE is a unique value identifying the challenge, should be shown to
    238   the user so that they can recognize it when they receive the TAN code.
    239 
    240 
    241   **Request:**
    242 
    243   :query response_type: Must be ``code``
    244   :query client_id: Identifier of the client.
    245   :query redirect_uri: URI-encoded redirection URI to use upon authorization.
    246   :query state: Arbitrary client state to associate with the request.
    247   :query scope: Not supported, any value is accepted.
    248   :query code_challenge: A string to enhance security using PKCE (available since **v3**).
    249   :query code_challenge_method: The method used for the code_challenge. Options are S256 (SHA-256) or plain (available since **v3**).
    250 
    251   **Response:**
    252 
    253   :http:statuscode:`200 OK`:
    254     The the response is
    255     a `ChallengeStatus`. Since protocol **v1**.
    256   :http:statuscode:`400 Bad Request`:
    257     The request does not follow the spec.
    258     The response will include error
    259     code, hint and detail. Since protocol **v1**.
    260   :http:statuscode:`404 Not found`:
    261     The service is unaware of a matching challenge.
    262     The response will include error
    263     code, hint and detail. Since protocol **v1**.
    264   :http:statuscode:`406 Not Acceptable`:
    265     The client ask for "text/html" and the backend installation does
    266     not include the required HTML templates.
    267   :http:statuscode:`500 Internal Server Error`:
    268     Server is not able to respond due to internal problems.
    269     The response will include error
    270     code, hint and detail. Since protocol **v1**.
    271 
    272   .. ts:def:: ChallengeStatus
    273 
    274     interface ChallengeStatus {
    275       // @deprecated since **v2**, use /config
    276       restrictions?: Object;
    277 
    278       // indicates if the given address cannot be changed anymore, the
    279       // form should be read-only if set to true.
    280       fix_address: boolean;
    281 
    282       // form values from the previous submission if available, details depend
    283       // on the ``ADDRESS_TYPE``, should be used to pre-populate the form
    284       last_address?: Object;
    285 
    286       // is the challenge already solved?
    287       solved: boolean;
    288 
    289       // number of times the address can still be changed, may or may not be
    290       // shown to the user
    291       changes_left: Integer;
    292 
    293       // when we would re-transmit the challenge the next
    294       // time (at the earliest) if requested by the user
    295       // only present if challenge already created
    296       // @since **v2**
    297       retransmission_time: Timestamp;
    298 
    299       // how many times might the PIN still be retransmitted
    300       // only present if challenge already created
    301       // @since **v2**
    302       pin_transmissions_left?: Integer;
    303 
    304       // how many times might the user still try entering the PIN code
    305       // only present if challenge already created
    306       // @since **v2**
    307       auth_attempts_left?: Integer;
    308     }
    309 
    310 
    311 .. _challenger-challenge:
    312 
    313 ---------
    314 Challenge
    315 ---------
    316 
    317 .. http:post:: /challenge/$NONCE
    318 
    319   This endpoint is used by the user-agent to submit the address to which a
    320   challenge should be sent by the challenger service.
    321 
    322   **Request:**
    323 
    324   Body should use the mime-type "application/x-www-form-urlencoded".
    325   The posted form data must contain an object that follow the restrictions
    326   defined in :ref:`config <challenger-config>`.
    327 
    328   **Response:**
    329 
    330   :http:statuscode:`200 OK`:
    331     The response is `ChallengeResponse`. Since protocol **v2**.
    332   :http:statuscode:`400 Bad Request`:
    333     The request does not follow the spec.
    334     The response will include error
    335     code, hint and detail. Since protocol **v1**.
    336   :http:statuscode:`404 Not Found`:
    337     The service is unaware of a matching challenge.
    338     The response will include error
    339     code, hint and detail. Since protocol **v1**.
    340   :http:statuscode:`406 Not Acceptable`:
    341     The client ask for "text/html" and the backend installation does
    342     not include the required HTML templates.
    343   :http:statuscode:`429 Too Many Requests`:
    344     There have been too many attempts to request challenge
    345     transmissions for this $NONCE. The user-agent should
    346     wait and (eventually) request a fresh nonce to be set
    347     up by the client.
    348     The response will include error
    349     code, hint and detail. Since protocol **v2**.
    350   :http:statuscode:`500 Internal Server Error`:
    351     Server is not able to respond due to internal problems.
    352     The response will include error
    353     code, hint and detail. Since protocol **v1**.
    354 
    355   .. ts:def:: ChallengeResponse
    356 
    357     // Union discriminated by the "type" field.
    358     type ChallengeResponse = ChallengeRedirect | ChallengeCreateResponse
    359 
    360   .. ts:def:: ChallengeRedirect
    361 
    362     // @since **v2**
    363     interface ChallengeRedirect {
    364       // Union discriminator field.
    365       type: "completed";
    366 
    367       // challenge is completed, use should redirect here
    368       redirect_url: string;
    369     }
    370 
    371   .. ts:def:: ChallengeCreateResponse
    372 
    373    interface ChallengeCreateResponse {
    374       // Union discriminator field.
    375       type: "created"
    376 
    377       // how many more attempts are allowed, might be shown to the user,
    378       // highlighting might be appropriate for low values such as 1 or 2 (the
    379       // form will never be used if the value is zero)
    380       attempts_left: Integer;
    381 
    382       // the address that is being validated, might be shown or not
    383       address: Object;
    384 
    385       // true if we just retransmitted the challenge, false if we sent a
    386       // challenge recently and thus refused to transmit it again this time;
    387       // might make a useful hint to the user
    388       transmitted: boolean;
    389 
    390       // @deprecated in **v2**, use retransmission_time
    391       next_tx_time?: string;
    392 
    393       // when we would re-transmit the challenge the next
    394       // time (at the earliest) if requested by the user
    395       // @since **v2**
    396       retransmission_time: Timestamp;
    397     }
    398 
    399 
    400 .. _challenger-solve:
    401 
    402 -----
    403 Solve
    404 -----
    405 
    406 .. http:post:: /solve/$NONCE
    407 
    408   Used by the user-agent to submit an answer to the challenge.  If the answer
    409   is correct, the user will be redirected to the client's redirect URI,
    410   otherwise the user may be given another chance to complete the process.
    411 
    412   **Request:**
    413 
    414   Body should use the mime-type "application/x-www-form-urlencoded".
    415   The posted form data must contain a "pin" field.
    416 
    417   **Response:**
    418 
    419   :http:statuscode:`200 OK`:
    420     If the request ask for application/json the response is
    421     a `ChallengeSolveResponse`. Since protocol **v2**.
    422   :http:statuscode:`302 Found`:
    423     Only possible if request didn't ask for application/json. Since protocol **v2**.
    424     The user is redirected to the redirect URI of the client to pass the
    425     grant to the client.  The target will be the redirect URI specified
    426     by the client (during registration and again upon ``/authorize``),
    427     plus a ``code`` argument with the authorization code, and the
    428     ``state`` argument from the ``/authorize`` endpoint.
    429   :http:statuscode:`400 Bad Request`:
    430     The request does not follow the spec.
    431     The response will include error
    432     code, hint and detail. Since protocol **v1**.
    433   :http:statuscode:`403 Forbidden`:
    434     The response is `InvalidPinResponse`. Since protocol **v1**.
    435   :http:statuscode:`404 Not found`:
    436     The service is unaware of a matching challenge.
    437     The response will include error
    438     code, hint and detail. Since protocol **v1**.
    439   :http:statuscode:`429 Too Many Requests`:
    440     There have been too many attempts to solve the challenge
    441     for this address (and $NONCE). The user-agent should
    442     either try a different address (or wait and (eventually)
    443     request a fresh nonce to be set up by the client).
    444     The response will include error
    445     code, hint and detail. Since protocol **v2**.
    446   :http:statuscode:`500 Internal Server Error`:
    447     Server is not able to respond due to internal problems.
    448     The response will include error
    449     code, hint and detail. Since protocol **v1**.
    450 
    451   .. ts:def:: ChallengeSolveResponse
    452 
    453     // Union discriminated by the "type" field.
    454     type ChallengeSolveResponse = ChallengeRedirect | InvalidPinResponse;
    455 
    456   .. ts:def:: InvalidPinResponse
    457 
    458     interface InvalidPinResponse {
    459       // Union discriminator field.
    460       type: "pending";
    461 
    462       // numeric Taler error code, should be shown to indicate the error
    463       // compactly for reporting to developers
    464       code: Integer;
    465 
    466       // human-readable Taler error code, should be shown for the user to
    467       // understand the error
    468       hint: string;
    469 
    470       // how many times is the user still allowed to change the address;
    471       // if 0, the user should not be shown a link to jump to the
    472       // address entry form
    473       addresses_left: Integer;
    474 
    475       // how many times might the PIN still be retransmitted
    476       pin_transmissions_left: Integer;
    477 
    478       // how many times might the user still try entering the PIN code
    479       auth_attempts_left: Integer;
    480 
    481       // if true, the PIN was not even evaluated as the user previously
    482       // exhausted the number of attempts
    483       exhausted: boolean;
    484 
    485       // if true, the PIN was not even evaluated as no challenge was ever
    486       // issued (the user must have skipped the step of providing their
    487       // address first!)
    488       no_challenge: boolean;
    489     }
    490 
    491 .. _challenger-auth:
    492 
    493 ----
    494 Auth
    495 ----
    496 
    497 .. http:post:: /token
    498 
    499   This is the token endpoint of the OAuth 2.0 specification.
    500   This endpoint is used by the client to provide its authorization code,
    501   demonstrating that it has the right to learn a particular user's validated
    502   address.  In return, the challenger service returns the access token.
    503   Renewal is not supported.
    504 
    505   **Request:**
    506 
    507   The request must include an ``application/www-form-urlencoded`` body
    508   specifying the ``client_id``, ``redirect_uri``, ``client_secret``, ``code``
    509   and ``grant_type``.  The ``grant_type`` must be set to
    510   ``authorization_code``.  The ``redirect_uri`` must match the URI from
    511   ``/authorize``. The ``code`` must be the authorization code that ``/solve``
    512   returned to the user.  The ``client_id`` and ``client_secret`` must match
    513   the usual client credentials. Since protocol **v3**, ``code_verifier`` can also be included.
    514 
    515   **Response:**
    516 
    517   Error responses follow RFC 6749, section 5.2 with an "error" field in JSON,
    518   as well as also returning GNU Taler style error messages.
    519 
    520   :http:statuscode:`200 OK`:
    521     The body will be a `ChallengerAuthResponse`
    522   :http:statuscode:`401 Unauthorized`:
    523     The ``code_verifier`` is not matching the saved ones. (Since **v3**)
    524   :http:statuscode:`403 Forbidden`:
    525     The credentials of the client are invalid.
    526   :http:statuscode:`404 Not found`:
    527     The service is unaware of a matching login process.
    528 
    529   **Details::**
    530 
    531   .. ts:def:: ChallengerAuthResponse
    532 
    533     interface ChallengerAuthResponse {
    534       // Token used to authenticate access in ``/info``.
    535       access_token: string;
    536 
    537       // Type of the access token.
    538       token_type: "Bearer";
    539 
    540       // Amount of time that an access token is valid (in seconds).
    541       expires_in: Integer;
    542 
    543     }
    544 
    545 
    546 .. _challenger-info:
    547 
    548 ----
    549 Info
    550 ----
    551 
    552 .. http:get:: /info
    553 
    554   This userinfo endpoint of the OAuth 2.0 specification.
    555   This endpoint is used by the client to obtain the user's validated address.
    556 
    557   **Request:**
    558 
    559   Must include the token returned to the client from the ``/token`` endpoint
    560   as a ``Bearer`` token in an ``Authorization`` header.
    561 
    562   **Response:**
    563 
    564   :http:statuscode:`200 OK`:
    565     The body contains the address as a `ChallengerInfoResponse`.
    566   :http:statuscode:`403 Forbidden`:
    567     The bearer token is missing or invalid (malformed).
    568   :http:statuscode:`404 Not found`:
    569     The bearer token is invalid (includes unknown or expired).
    570 
    571   **Details::**
    572 
    573   .. ts:def:: ChallengerInfoResponse
    574 
    575     interface ChallengerInfoResponse {
    576 
    577       // Unique ID of the record within Challenger
    578       // (identifies the rowid of the token).
    579       id: Integer;
    580 
    581       // Address that was validated.
    582       // Key-value pairs, details depend on the
    583       // address_type.
    584       address: Object;
    585 
    586      // Type of the address.
    587       address_type: string;
    588 
    589       // How long do we consider the address to be
    590       // valid for this user.
    591       expires: Timestamp;
    592 
    593     }