summaryrefslogtreecommitdiff
path: root/core/api-challenger.rst
blob: f86319772c4b906a9aa2ae84b4d74a1511b64b04 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
..
  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;

    }

.. _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 {
      // 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;

      // 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;
    }


.. _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 `ChallengeCreateResponse`. Since protocol **v1**.
    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:`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:: 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;

      // timestamp explaining when we would re-transmit the challenge the next
      // time (at the earliest) if requested by the user
      next_tx_time: String;

    }



.. _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:`302 Found`:
    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:: 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;

    }