diff options
Diffstat (limited to 'doc/sphinx/reducer.rst')
-rw-r--r-- | doc/sphinx/reducer.rst | 1656 |
1 files changed, 1656 insertions, 0 deletions
diff --git a/doc/sphinx/reducer.rst b/doc/sphinx/reducer.rst new file mode 100644 index 0000000..e5f1699 --- /dev/null +++ b/doc/sphinx/reducer.rst | |||
@@ -0,0 +1,1656 @@ | |||
1 | .. | ||
2 | This file is part of Anastasis | ||
3 | Copyright (C) 2019-2021 Anastasis SARL | ||
4 | |||
5 | Anastasis 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 | Anastasis 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 | Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/> | ||
15 | |||
16 | @author Christian Grothoff | ||
17 | @author Dominik Meister | ||
18 | @author Dennis Neufeld | ||
19 | |||
20 | ----------- | ||
21 | Reducer API | ||
22 | ----------- | ||
23 | |||
24 | This section describes the Anastasis Reducer API which is used by client applications | ||
25 | to store or load the different states the client application can have. | ||
26 | The reducer takes a state_ in JSON syntax and returns the new state in JSON syntax. | ||
27 | |||
28 | For example a **state** may take the following structure: | ||
29 | |||
30 | .. code-block:: json | ||
31 | |||
32 | { | ||
33 | "backup_state": "CONTINENT_SELECTING", | ||
34 | "continents": [ | ||
35 | "Europe", | ||
36 | "North_America" | ||
37 | ] | ||
38 | } | ||
39 | |||
40 | The new state depends on the previous one and on the transition action_ with its | ||
41 | arguments given to the reducer. A **transition argument** also is a statement in JSON syntax: | ||
42 | |||
43 | .. code-block:: json | ||
44 | |||
45 | { | ||
46 | "continent": "Europe" | ||
47 | } | ||
48 | |||
49 | The new state returned by the reducer with the state and transition argument defined | ||
50 | above would look like following for the transition action_ ``select_continent``: | ||
51 | |||
52 | .. code-block:: json | ||
53 | |||
54 | { | ||
55 | "backup_state": "COUNTRY_SELECTING", | ||
56 | "continents": [ | ||
57 | "Europe", | ||
58 | "North_America" | ||
59 | ], | ||
60 | "selected_continent": "Europe", | ||
61 | "countries": [ | ||
62 | { | ||
63 | "code": "ch", | ||
64 | "name": "Switzerland", | ||
65 | "continent": "Europe", | ||
66 | "name_i18n": { | ||
67 | "de_DE": "Schweiz", | ||
68 | "de_CH": "Schwiiz", | ||
69 | "fr": "Suisse", | ||
70 | "en": "Swiss" | ||
71 | }, | ||
72 | "currency": "CHF" | ||
73 | }, | ||
74 | { | ||
75 | "code": "de", | ||
76 | "name": "Germany", | ||
77 | "continent": "Europe", | ||
78 | "continent_i18n": { | ||
79 | "de": "Europa" | ||
80 | }, | ||
81 | "name_i18n": { | ||
82 | "de_DE": "Deutschland", | ||
83 | "de_CH": "Deutschland", | ||
84 | "fr": "Allemagne", | ||
85 | "en": "Germany" | ||
86 | }, | ||
87 | "currency": "EUR" | ||
88 | } | ||
89 | ] | ||
90 | } | ||
91 | |||
92 | States | ||
93 | ^^^^^^ | ||
94 | |||
95 | Overall, the reducer knows the following states: | ||
96 | |||
97 | - **ERROR**: The transition led to an error. No further transitions are possible from | ||
98 | this state, but the client may want to continue from a previous state. | ||
99 | - **CONTINENT_SELECTING**: The user should specify the continent where they are living, | ||
100 | so that we can show a list of countries to choose from. | ||
101 | - **COUNTRY_SELECTING**: The user should specify the country where they are living, | ||
102 | so that we can determine appropriate attributes, currencies and Anastasis | ||
103 | providers. | ||
104 | - **USER_ATTRIBUTES_COLLECTING**: The user should provide the country-specific personal | ||
105 | attributes. | ||
106 | - **AUTHENTICATIONS_EDITING**: The user should add authentication methods to be used | ||
107 | during recovery. | ||
108 | - **POLICIES_REVIEWING**: The user should review the recovery policies. | ||
109 | - **SECRET_EDITING**: The user should edit the secret to be backed up. | ||
110 | - **TRUTHS_PAYING**: The user needs to pay for one or more uploads of data associated | ||
111 | with an authentication method. | ||
112 | - **POLICIES_PAYING**: The user needs to pay for storing the recovery policy document. | ||
113 | - **BACKUP_FINISHED**: A backup has been successfully generated. | ||
114 | - **SECRET_SELECTING**: The user needs to select a recovery policy document with | ||
115 | the secret that is to be recovered. | ||
116 | - **CHALLENGE_SELECTING**: The user needs to select an authorization challenge to | ||
117 | proceed with recovery. | ||
118 | - **CHALLENGE_PAYING**: The user needs to pay to proceed with the authorization challenge. | ||
119 | - **CHALLENGE_SOLVING**: The user needs to solve the authorization challenge. | ||
120 | - **RECOVERY_FINISHED**: The secret of the user has been recovered. | ||
121 | |||
122 | State names: | ||
123 | |||
124 | - In SELECTING-states, the user has to choose one value out of a predefined set of values (for example a continent out of a set of continents). | ||
125 | - In COLLECTING-states, the user has to give certain values. | ||
126 | - In EDITING-states, the user is free to choose which values he wants to give. | ||
127 | - In REVEIWING-states, the user may make a few choices, but primarily is expected to affirm something. | ||
128 | - in PAYING-states, the user must make a payment. | ||
129 | - in FINISHED-states, the operation has definitively concluded. | ||
130 | |||
131 | |||
132 | Backup Reducer | ||
133 | ^^^^^^^^^^^^^^ | ||
134 | .. _state: | ||
135 | .. _action: | ||
136 | .. figure:: anastasis_reducer_backup.png | ||
137 | :name: fig-anastasis_reducer_backup | ||
138 | :alt: fig-anastasis_reducer_backup | ||
139 | :scale: 75 % | ||
140 | :align: center | ||
141 | |||
142 | Backup states and their transitions. | ||
143 | |||
144 | |||
145 | The illustration above shows the different states the reducer can have during a backup | ||
146 | process. | ||
147 | |||
148 | |||
149 | Recovery Reducer | ||
150 | ^^^^^^^^^^^^^^^^ | ||
151 | .. figure:: anastasis_reducer_recovery.png | ||
152 | :name: fig-anastasis_reducer_recovery | ||
153 | :alt: fig-anastasis_reducer_recovery | ||
154 | :scale: 75 % | ||
155 | :align: center | ||
156 | |||
157 | Recovery states and their transitions. | ||
158 | |||
159 | |||
160 | The illustration above shows the different states the reducer can have during a recovery | ||
161 | process. | ||
162 | |||
163 | |||
164 | Reducer transitions | ||
165 | ^^^^^^^^^^^^^^^^^^^ | ||
166 | In the following, the individual transitions will be specified in more detail. | ||
167 | Note that we only show fields added by the reducer, typically the previous | ||
168 | state is preserved to enable "back" transitions to function smoothly. | ||
169 | |||
170 | |||
171 | Initial state | ||
172 | ------------- | ||
173 | |||
174 | The initial states for backup and recovery processes are: | ||
175 | |||
176 | **Initial backup state:** | ||
177 | |||
178 | .. code-block:: json | ||
179 | |||
180 | { | ||
181 | "backup_state": "CONTINENT_SELECTING", | ||
182 | "continents": [ | ||
183 | "Europe", | ||
184 | "North America" | ||
185 | ] | ||
186 | } | ||
187 | |||
188 | |||
189 | **Initial recovery state:** | ||
190 | |||
191 | .. code-block:: json | ||
192 | |||
193 | { | ||
194 | "recovery_state": "CONTINENT_SELECTING", | ||
195 | "continents": [ | ||
196 | "Europe", | ||
197 | "North America" | ||
198 | ] | ||
199 | } | ||
200 | |||
201 | Here, "continents" is an array of English strings with the names of the | ||
202 | continents which contain countries for which Anastasis could function (based | ||
203 | on having providers that are known to operate and rules being provided for | ||
204 | user attributes from those countries). | ||
205 | |||
206 | For internationalization, another field ``continents_i18n`` may be present. | ||
207 | This field would be a map of language names to arrays of translated | ||
208 | continent names: | ||
209 | |||
210 | .. code-block:: json | ||
211 | |||
212 | { | ||
213 | "recovery_state": "CONTINENT_SELECTING", | ||
214 | "continents": [ | ||
215 | "Europe", | ||
216 | "North America" | ||
217 | ] | ||
218 | "continents_i18n": | ||
219 | { | ||
220 | "de_DE" : [ | ||
221 | "Europa", | ||
222 | "Nordamerika" | ||
223 | ], | ||
224 | "de_CH" : [ | ||
225 | "Europa", | ||
226 | "Nordamerika" | ||
227 | ] | ||
228 | } | ||
229 | } | ||
230 | |||
231 | Translations must be given in the same order as the main English array. | ||
232 | |||
233 | |||
234 | Common transitions | ||
235 | ------------------ | ||
236 | |||
237 | **select_continent:** | ||
238 | |||
239 | Here the user specifies the continent they live on. Arguments (example): | ||
240 | |||
241 | .. code-block:: json | ||
242 | |||
243 | { | ||
244 | "continent": "Europe" | ||
245 | } | ||
246 | |||
247 | The continent must be given using the English name from the ``continents`` array. | ||
248 | Using a translated continent name is invalid and may result in failure. | ||
249 | |||
250 | The reducer returns an updated state with a list of countries to choose from, | ||
251 | for example: | ||
252 | |||
253 | .. code-block:: json | ||
254 | |||
255 | { | ||
256 | "backup_state": "COUNTRY_SELECTING", | ||
257 | "selected_continent": "Europe", | ||
258 | "countries": [ | ||
259 | { | ||
260 | "code": "ch", | ||
261 | "name": "Switzerland", | ||
262 | "continent": "Europe", | ||
263 | "name_i18n": { | ||
264 | "de_DE": "Schweiz", | ||
265 | "de_CH": "Schwiiz", | ||
266 | "fr": "Suisse", | ||
267 | "en": "Swiss" | ||
268 | }, | ||
269 | "currency": "CHF" | ||
270 | }, | ||
271 | { | ||
272 | "code": "de", | ||
273 | "name": "Germany", | ||
274 | "continent": "Europe", | ||
275 | "continent_i18n": { | ||
276 | "de": "Europa" | ||
277 | }, | ||
278 | "name_i18n": { | ||
279 | "de_DE": "Deutschland", | ||
280 | "de_CH": "Deutschland", | ||
281 | "fr": "Allemagne", | ||
282 | "en": "Germany" | ||
283 | }, | ||
284 | "currency": "EUR" | ||
285 | } | ||
286 | ] | ||
287 | } | ||
288 | |||
289 | Here ``countries`` is an array of countries on the ``selected_continent``. For | ||
290 | each country, the ``code`` is the ISO 3166-1 alpha-2 country code. The | ||
291 | ``continent`` is only present because some countries span continents, the | ||
292 | information is redundant and will always match ``selected_continent``. The | ||
293 | ``name`` is the name of the country in English, internationalizations of the | ||
294 | name may be provided in ``name_i18n``. The ``currency`` is **an** official | ||
295 | currency of the country, if a country has multiple currencies, it may appear | ||
296 | multiple times in the list. In this case, the user should select the entry | ||
297 | with the currency they intend to pay with. It is also possible for users | ||
298 | to select a currency that does not match their country, but user interfaces | ||
299 | should by default try to use currencies that match the user's residence. | ||
300 | |||
301 | |||
302 | **select_country:** | ||
303 | |||
304 | Selects the country (via the country code) and specifies the currency. | ||
305 | The latter is needed as some countries have more than one currency, | ||
306 | and some use-cases may also involve users insisting on paying with | ||
307 | foreign currency. | ||
308 | |||
309 | Arguments (example): | ||
310 | |||
311 | .. code-block:: json | ||
312 | |||
313 | { | ||
314 | "country_code": "de", | ||
315 | "currency": "EUR" | ||
316 | } | ||
317 | |||
318 | The ``country_code`` must be an ISO 3166-1 alpha-2 country code from | ||
319 | the array of ``countries`` of the reducer's state. The ``currency`` | ||
320 | field must be a valid currency accepted by the Taler payment system. | ||
321 | |||
322 | The reducer returns a new state with the list of attributes the | ||
323 | user is expected to provide, as well as possible authentication | ||
324 | providers that accept payments in the selected currency: | ||
325 | |||
326 | .. code-block:: json | ||
327 | |||
328 | { | ||
329 | "backup_state": "USER_ATTRIBUTES_COLLECTING", | ||
330 | "selected_country": "de", | ||
331 | "currency": "EUR", | ||
332 | "required_attributes": [ | ||
333 | { | ||
334 | "type": "string", | ||
335 | "name": "full_name", | ||
336 | "label": "Full name", | ||
337 | "label_i18n": { | ||
338 | "de_DE": "Vollstaendiger Name", | ||
339 | "de_CH": "Vollstaendiger. Name", | ||
340 | "fr": "Nom complet", | ||
341 | "en": "Full name" | ||
342 | }, | ||
343 | "widget": "anastasis_gtk_ia_full_name", | ||
344 | "uuid" : "9e8f463f-575f-42cb-85f3-759559997331" | ||
345 | }, | ||
346 | { | ||
347 | "type": "date", | ||
348 | "name": "birthdate", | ||
349 | "label": "Birthdate", | ||
350 | "label_i18n": { | ||
351 | "de_DE": "Geburtsdatum", | ||
352 | "de_CH": "Geburtsdatum", | ||
353 | "fr": "Date de naissance", | ||
354 | "en": "Birthdate" | ||
355 | }, | ||
356 | "uuid" : "83d655c7-bdb6-484d-904e-80c1058c8854" | ||
357 | "widget": "anastasis_gtk_ia_birthdate" | ||
358 | }, | ||
359 | { | ||
360 | "type": "string", | ||
361 | "name": "tax_number", | ||
362 | "label": "Taxpayer identification number", | ||
363 | "label_i18n":{ | ||
364 | "de_DE": "Steuerliche Identifikationsnummer", | ||
365 | "de_CH": "Steuerliche Identifikationsnummer", | ||
366 | "en": "German taxpayer identification number" | ||
367 | }, | ||
368 | "widget": "anastasis_gtk_ia_tax_de", | ||
369 | "uuid": "dae48f85-e3ff-47a4-a4a3-ed981ed8c3c6", | ||
370 | "validation-regex": "^[0-9]{11}$", | ||
371 | "validation-logic": "DE_TIN_check" | ||
372 | }, | ||
373 | { | ||
374 | "type": "string", | ||
375 | "name": "social_security_number", | ||
376 | "label": "Social security number", | ||
377 | "label_i18n": { | ||
378 | "de_DE": "Sozialversicherungsnummer", | ||
379 | "de_CH": "Sozialversicherungsnummer", | ||
380 | "fr": "Numéro de sécurité sociale", | ||
381 | "en": "Social security number" | ||
382 | }, | ||
383 | "widget": "anastasis_gtk_ia_ssn", | ||
384 | "validation-regex": "^[0-9]{8}[[:upper:]][0-9]{3}$", | ||
385 | "validation-logic": "DE_SVN_check" | ||
386 | "optional" : true | ||
387 | } | ||
388 | ], | ||
389 | "authentication_providers": { | ||
390 | "http://localhost:8089/": { | ||
391 | "http_status": 200, | ||
392 | "methods": [ | ||
393 | { "type" : "question", | ||
394 | "usage_fee" : "EUR:0.0" }, | ||
395 | { "type" : "sms", | ||
396 | "usage_fee" : "EUR:0.5" } | ||
397 | ], | ||
398 | "annual_fee": "EUR:4.99", | ||
399 | "truth_upload_fee": "EUR:4.99", | ||
400 | "liability_limit": "EUR:1", | ||
401 | "currency": "EUR", | ||
402 | "truth_lifetime": { "d_ms" : 50000000 }, | ||
403 | "storage_limit_in_megabytes": 1, | ||
404 | "provider_name": "Anastasis 4", | ||
405 | "salt": "CXAPCKSH9D3MYJTS9536RHJHCW" | ||
406 | }, | ||
407 | "http://localhost:8088/": { | ||
408 | "http_status": 200, | ||
409 | "methods": [ | ||
410 | { "type" : "question", | ||
411 | "usage_fee" : "EUR:0.01" }, | ||
412 | { "type" : "sms", | ||
413 | "usage_fee" : "EUR:0.55" } | ||
414 | ], | ||
415 | "annual_fee": "EUR:0.99", | ||
416 | "truth_upload_fee": "EUR:3.99", | ||
417 | "liability_limit": "EUR:1", | ||
418 | "currency": "EUR", | ||
419 | "truth_lifetime": { "d_ms" : 50000000 }, | ||
420 | "storage_limit_in_megabytes": 1, | ||
421 | "provider_name": "Anastasis 4", | ||
422 | "salt": "CXAPCKSH9D3MYJTS9536RHJHCW" | ||
423 | } | ||
424 | } | ||
425 | } | ||
426 | |||
427 | The array of ``required_attributes`` contains attributes about the user | ||
428 | that must be provided includes: | ||
429 | |||
430 | - **type**: The type of the attribute, for now only ``string`` and ``date`` are | ||
431 | supported. | ||
432 | - **name**: The name of the attribute, this is the key under which the | ||
433 | attribute value must be provided later. The name must be unique per response. | ||
434 | - **label**: A human-readable description of the attribute in English. | ||
435 | Translated descriptions may be provided under **label_i18n**. | ||
436 | - **uuid**: A UUID that uniquely identifies identical attributes across | ||
437 | different countries. Useful to preserve values should the user enter | ||
438 | some attributes, and then switch to another country. Note that | ||
439 | attributes must not be preserved if they merely have the same **name**, | ||
440 | only the **uuid** will be identical if the semantics is identical. | ||
441 | - **widget**: An optional name of a widget that is known to nicely render | ||
442 | the attribute entry in user interfaces where named widgets are | ||
443 | supported. | ||
444 | - **validation-regex**: An optional extended POSIX regular expression | ||
445 | that is to be used to validate (string) inputs to ensure they are | ||
446 | well-formed. | ||
447 | - **validation-logic**: Optional name of a function that should be called | ||
448 | to validate the input. If the function is not known to the particular | ||
449 | client, the respective validation can be skipped (at the expense of | ||
450 | typos by users not being detected, possibly rendering secrets | ||
451 | irrecoverable). | ||
452 | - **optional**: Optional boolean field that, if ``true``, indicates that | ||
453 | this attribute is not actually required but optional and users MAY leave | ||
454 | it blank in case they do not have the requested information. Used for | ||
455 | common fields that apply to some large part of the population but are | ||
456 | not sufficiently universal to be actually required. | ||
457 | |||
458 | The authentication providers are listed under a key that is the | ||
459 | base URL of the service. For each provider, the following | ||
460 | information is provided if the provider was successfully contacted: | ||
461 | |||
462 | - **http_status**: HTTP status code, always ``200`` on success. | ||
463 | - **methods**: Array of authentication methods supported by this | ||
464 | provider. Includes the **type** of the authentication method | ||
465 | and the **usage_fee** (how much the user must pay for authorization | ||
466 | using this method during recovery). | ||
467 | - **annual_fee**: Fee the provider charges to store the recovery | ||
468 | policy for one year. | ||
469 | - **truth_upload_fee**: Fee the provider charges to store a key share. | ||
470 | - **liability_limit**: Amount the provider can be held liable for in | ||
471 | case a key share or recovery document cannot be recovered due to | ||
472 | provider failures. | ||
473 | - **currency**: Currency in which the provider wants to be paid, | ||
474 | will match all of the fees. | ||
475 | - **storage_limit_in_megabytes**: Maximum size of an upload (for | ||
476 | both recovery document and truth data) in megabytes. | ||
477 | - **provider_name**: Human-readable name of the provider's business. | ||
478 | - **salt**: Salt value used by the provider, used to derive the | ||
479 | user's identity at this provider. Should be unique per provider, | ||
480 | and must never change for a given provider. The salt is | ||
481 | base32 encoded. | ||
482 | |||
483 | If contacting the provider failed, the information returned is: | ||
484 | |||
485 | - **http_status**: HTTP status code (if available, possibly 0 if | ||
486 | we did not even obtain an HTTP response). | ||
487 | - **error_code**: Taler error code, never 0. | ||
488 | |||
489 | |||
490 | **add_provider**: | ||
491 | |||
492 | This operation can be performed in state ``USER_ATTRIBUTES_COLLECTING``. It | ||
493 | adds one or more Anastasis providers to the list of providers the reducer | ||
494 | should henceforth consider. Note that removing providers is not possible at | ||
495 | this time. | ||
496 | |||
497 | Here, the client must provide an array with the base URLs of the | ||
498 | providers to add, for example: | ||
499 | |||
500 | .. code-block:: json | ||
501 | |||
502 | { | ||
503 | "urls": [ | ||
504 | "http://localhost:8888/", | ||
505 | "http://localhost:8089/" | ||
506 | ] | ||
507 | } | ||
508 | |||
509 | Note that existing providers will remain in the state. The following is an | ||
510 | example for an expected new state where the service on port 8089 is | ||
511 | unreachable, the service on port 8088 was previously known, and service on | ||
512 | port 8888 was now added: | ||
513 | |||
514 | .. code-block:: json | ||
515 | |||
516 | { | ||
517 | "backup_state": "USER_ATTRIBUTES_COLLECTING", | ||
518 | "authentication_providers": { | ||
519 | "http://localhost:8089/": { | ||
520 | "error_code": 11, | ||
521 | "http_status": 0 | ||
522 | }, | ||
523 | "http://localhost:8088/": { | ||
524 | "http_status": 200, | ||
525 | "methods": [ | ||
526 | { "type" : "question", | ||
527 | "usage_fee" : "EUR:0.01" }, | ||
528 | { "type" : "sms", | ||
529 | "usage_fee" : "EUR:0.55" } | ||
530 | ], | ||
531 | "annual_fee": "EUR:0.99", | ||
532 | "truth_upload_fee": "EUR:3.99", | ||
533 | "liability_limit": "EUR:1", | ||
534 | "currency": "EUR", | ||
535 | "truth_lifetime": { "d_ms" : 50000000 }, | ||
536 | "storage_limit_in_megabytes": 1, | ||
537 | "provider_name": "Anastasis 4", | ||
538 | "salt": "CXAPCKSH9D3MYJTS9536RHJHCW" | ||
539 | } | ||
540 | "http://localhost:8888/": { | ||
541 | "methods": [ | ||
542 | { "type" : "question", | ||
543 | "usage_fee" : "EUR:0.01" }, | ||
544 | { "type" : "sms", | ||
545 | "usage_fee" : "EUR:0.55" } | ||
546 | ], | ||
547 | "annual_fee": "EUR:0.99", | ||
548 | "truth_upload_fee": "EUR:3.99", | ||
549 | "liability_limit": "EUR:1", | ||
550 | "currency": "EUR", | ||
551 | "truth_lifetime": { "d_ms" : 50000000 }, | ||
552 | "storage_limit_in_megabytes": 1, | ||
553 | "provider_name": "Anastasis 42", | ||
554 | "salt": "BXAPCKSH9D3MYJTS9536RHJHCX" | ||
555 | } | ||
556 | } | ||
557 | } | ||
558 | |||
559 | |||
560 | |||
561 | Backup transitions | ||
562 | ------------------ | ||
563 | |||
564 | **enter_user_attributes:** | ||
565 | |||
566 | This transition provides the user's personal attributes. The specific set of | ||
567 | attributes required depends on the country of residence of the user. Some | ||
568 | attributes may be optional, in which case they should be omitted entirely | ||
569 | (that is, not simply be set to ``null`` or an empty string). Example | ||
570 | arguments would be: | ||
571 | |||
572 | .. code-block:: json | ||
573 | |||
574 | { | ||
575 | "identity_attributes": { | ||
576 | "full_name": "Max Musterman", | ||
577 | "social_security_number": "123456789", | ||
578 | "birthdate": "2000-01-01", | ||
579 | "birthplace": "Earth" | ||
580 | } | ||
581 | } | ||
582 | |||
583 | Note that at this stage, the state machines between backup and | ||
584 | recovery diverge and the ``recovery_state`` will begin to look | ||
585 | very different from the ``backup_state``. | ||
586 | |||
587 | For backups, if all required attributes are present, the reducer will | ||
588 | transition to an ``AUTHENTICATIONS_EDITING`` state with the attributes added | ||
589 | to it: | ||
590 | |||
591 | .. code-block:: json | ||
592 | |||
593 | { | ||
594 | "backup_state": "AUTHENTICATIONS_EDITING", | ||
595 | "identity_attributes": { | ||
596 | "full_name": "Max Musterman", | ||
597 | "social_security_number": "123456789", | ||
598 | "birthdate": "2000-01-01", | ||
599 | "birthplace": "Earth" | ||
600 | } | ||
601 | } | ||
602 | |||
603 | If required attributes are missing, do not match the required regular | ||
604 | expression, or fail the custom validation logic, the reducer SHOULD transition | ||
605 | to an error state indicating what was wrong about the input. A reducer that | ||
606 | does not support some specific validation logic MAY accept the invalid input | ||
607 | and proceed anyway. The error state will include a Taler error code that | ||
608 | is specific to the failure, and optional details. Example: | ||
609 | |||
610 | .. code-block:: json | ||
611 | |||
612 | { | ||
613 | "backup_state": "ERROR", | ||
614 | "code": 8404, | ||
615 | "hint": "An input did not match the regular expression.", | ||
616 | "detail": "social_security_number" | ||
617 | } | ||
618 | |||
619 | Clients may safely repeat this transition to validate the user's inputs | ||
620 | until they satisfy all of the constraints. This way, the user interface | ||
621 | does not have to perform the input validation directly. | ||
622 | |||
623 | |||
624 | **add_authentication**: | ||
625 | |||
626 | This transition adds an authentication method. The method must be supported | ||
627 | by one or more providers that are included in the current state. Adding an | ||
628 | authentication method requires specifying the ``type`` and ``instructions`` to | ||
629 | be given to the user. The ``challenge`` is encrypted and stored at the | ||
630 | Anastasis provider. The specific semantics of the value depend on the | ||
631 | ``type``. Typical challenges values are a phone number (to send an SMS to), | ||
632 | an e-mail address (to send a PIN code to) or the answer to a security | ||
633 | question. Note that these challenge values will still be encrypted (and | ||
634 | possibly hashed) before being given to the Anastasis providers. | ||
635 | |||
636 | Note that the ``challenge`` must be given in Crockford Base32 encoding, as it | ||
637 | MAY include binary data (such as a photograph of the user). In the latter | ||
638 | case, the optional ``mime_type`` field must be provided to give the MIME type | ||
639 | of the value encoded in ``challenge``. | ||
640 | |||
641 | .. code-block:: json | ||
642 | |||
643 | { | ||
644 | "authentication_method": | ||
645 | { | ||
646 | "type": "question", | ||
647 | "mime_type" : "text/plain", | ||
648 | "instructions" : "What is your favorite GNU package?", | ||
649 | "challenge" : "E1QPPS8A", | ||
650 | } | ||
651 | } | ||
652 | |||
653 | If the information provided is valid, the reducer will add the new | ||
654 | authentication method to the array of authentication methods: | ||
655 | |||
656 | .. code-block:: json | ||
657 | |||
658 | { | ||
659 | "backup_state": "AUTHENTICATIONS_EDITING", | ||
660 | "authentication_methods": [ | ||
661 | { | ||
662 | "type": "question", | ||
663 | "mime_type" : "text/plain", | ||
664 | "instructions" : "What is your favorite GNU package?", | ||
665 | "challenge" : "E1QPPS8A", | ||
666 | }, | ||
667 | { | ||
668 | "type": "email", | ||
669 | "instructions" : "E-mail to user@*le.com", | ||
670 | "challenge": "ENSPAWJ0CNW62VBGDHJJWRVFDM50" | ||
671 | } | ||
672 | ] | ||
673 | } | ||
674 | |||
675 | |||
676 | **delete_authentication**: | ||
677 | |||
678 | This transition can be used to remove an authentication method from the | ||
679 | array of authentication methods. It simply requires the index of the | ||
680 | authentication method to remove. Note that the array is 0-indexed: | ||
681 | |||
682 | .. code-block:: json | ||
683 | |||
684 | { | ||
685 | "authentication_method": 1 | ||
686 | } | ||
687 | |||
688 | Assuming we begin with the state from the example above, this would | ||
689 | remove the ``email`` authentication method, resulting in the following | ||
690 | response: | ||
691 | |||
692 | .. code-block:: json | ||
693 | |||
694 | { | ||
695 | "backup_state": "AUTHENTICATIONS_EDITING", | ||
696 | "authentication_methods": [ | ||
697 | { | ||
698 | "type": "question", | ||
699 | "mime_type" : "text/plain", | ||
700 | "instructions" : "What is your favorite GNU package?", | ||
701 | "challenge" : "gdb", | ||
702 | } | ||
703 | ] | ||
704 | } | ||
705 | |||
706 | If the index is invalid, the reducer will instead | ||
707 | transition into an ``ERROR`` state. | ||
708 | |||
709 | |||
710 | **next** (from ``AUTHENTICATIONS_EDITING``): | ||
711 | |||
712 | This transition confirms that the user has finished adding (or removing) | ||
713 | authentication methods, and that the system should now automatically compute | ||
714 | a set of reasonable recovery policies. | ||
715 | |||
716 | This transition does not take any mandatory arguments. Optional arguments can | ||
717 | be provided to upload the recovery document only to a specific subset of the | ||
718 | providers: | ||
719 | |||
720 | .. code-block:: json | ||
721 | |||
722 | { | ||
723 | "providers": [ | ||
724 | "http://localhost:8088/", | ||
725 | "http://localhost:8089/" | ||
726 | ] | ||
727 | } | ||
728 | |||
729 | The resulting state provides the suggested recovery policies in a way suitable | ||
730 | for presentation to the user: | ||
731 | |||
732 | .. code-block:: javascript | ||
733 | |||
734 | { | ||
735 | "backup_state": "POLICIES_REVIEWING", | ||
736 | "policy_providers" : [ | ||
737 | { "provider_url" : "http://localhost:8088/" }, | ||
738 | { "provider_url" : "http://localhost:8089/" } | ||
739 | ], | ||
740 | "policies": [ | ||
741 | { | ||
742 | "methods": [ | ||
743 | { | ||
744 | "authentication_method": 0, | ||
745 | "provider": "http://localhost:8088/" | ||
746 | }, | ||
747 | { | ||
748 | "authentication_method": 1, | ||
749 | "provider": "http://localhost:8089/" | ||
750 | }, | ||
751 | { | ||
752 | "authentication_method": 2, | ||
753 | "provider": "http://localhost:8087/" | ||
754 | } | ||
755 | ] | ||
756 | }, | ||
757 | { | ||
758 | "methods": [ | ||
759 | { | ||
760 | "authentication_method": 0, | ||
761 | "provider": "http://localhost:8088/" | ||
762 | }, | ||
763 | { | ||
764 | "authentication_method": 1, | ||
765 | "provider": "http://localhost:8089/" | ||
766 | }, | ||
767 | { | ||
768 | "authentication_method": 3, | ||
769 | "provider": "http://localhost:8089/" | ||
770 | } | ||
771 | ] | ||
772 | } | ||
773 | ] | ||
774 | } | ||
775 | |||
776 | For each recovery policy, the state includes the specific details of which | ||
777 | authentication ``methods`` must be solved to recovery the secret using this | ||
778 | policy. The ``methods`` array specifies the index of the | ||
779 | ``authentication_method`` in the ``authentication_methods`` array, as well as | ||
780 | the provider that was selected to supervise this authentication. | ||
781 | |||
782 | If no authentication method was provided, the reducer will transition into an | ||
783 | ``ERROR`` state instead of suggesting policies. | ||
784 | |||
785 | |||
786 | **add_policy**: | ||
787 | |||
788 | Using this transition, the user can add an additional recovery policy to the | ||
789 | state. The argument format is the same that is used in the existing state. | ||
790 | An example for a possible argument would thus be: | ||
791 | |||
792 | .. code-block:: javascript | ||
793 | |||
794 | { | ||
795 | "policy": [ | ||
796 | { | ||
797 | "authentication_method": 1, | ||
798 | "provider": "http://localhost:8088/" | ||
799 | }, | ||
800 | { | ||
801 | "authentication_method": 3, | ||
802 | "provider": "http://localhost:8089/" | ||
803 | } | ||
804 | ] | ||
805 | } | ||
806 | |||
807 | Note that the specified providers must already be in the | ||
808 | ``authentication_providers`` of the state. You cannot add new providers at | ||
809 | this stage. The reducer will simply attempt to append the suggested policy to | ||
810 | the "policies" array, returning an updated state: | ||
811 | |||
812 | .. code-block:: json | ||
813 | |||
814 | { | ||
815 | "backup_state": "POLICIES_REVIEWING", | ||
816 | "policies": [ | ||
817 | { | ||
818 | "methods": [ | ||
819 | { | ||
820 | "authentication_method": 0, | ||
821 | "provider": "http://localhost:8089/" | ||
822 | }, | ||
823 | { | ||
824 | "authentication_method": 1, | ||
825 | "provider": "http://localhost:8088/" | ||
826 | } | ||
827 | ] | ||
828 | }, | ||
829 | { | ||
830 | "methods": [ | ||
831 | { | ||
832 | "authentication_method": 0, | ||
833 | "provider": "http://localhost:8089/" | ||
834 | }, | ||
835 | { | ||
836 | "authentication_method": 2, | ||
837 | "provider": "http://localhost:8088/" | ||
838 | } | ||
839 | ] | ||
840 | }, | ||
841 | { | ||
842 | "methods": [ | ||
843 | { | ||
844 | "authentication_method": 1, | ||
845 | "provider": "http://localhost:8089/" | ||
846 | }, | ||
847 | { | ||
848 | "authentication_method": 2, | ||
849 | "provider": "http://localhost:8088/" | ||
850 | } | ||
851 | ] | ||
852 | }, | ||
853 | { | ||
854 | "methods": [ | ||
855 | { | ||
856 | "authentication_method": 1, | ||
857 | "provider": "http://localhost:8088/" | ||
858 | }, | ||
859 | { | ||
860 | "authentication_method": 3, | ||
861 | "provider": "http://localhost:8089/" | ||
862 | } | ||
863 | ] | ||
864 | } | ||
865 | ] | ||
866 | } | ||
867 | |||
868 | If the new policy is invalid, for example because it adds an unknown | ||
869 | authentication method, or the selected provider does not support the type of | ||
870 | authentication, the reducer will transition into an ``ERROR`` state instead of | ||
871 | adding the new policy. | ||
872 | |||
873 | |||
874 | **update_policy**: | ||
875 | |||
876 | Using this transition, the user can modify an existing recovery policy | ||
877 | in the state. | ||
878 | The argument format is the same that is used in **add_policy**, | ||
879 | except there is an additional key ``policy_index`` which | ||
880 | identifies the policy to modify. | ||
881 | An example for a possible argument would thus be: | ||
882 | |||
883 | .. code-block:: javascript | ||
884 | |||
885 | { | ||
886 | "policy_index" : 1, | ||
887 | "policy": [ | ||
888 | { | ||
889 | "authentication_method": 1, | ||
890 | "provider": "http://localhost:8088/" | ||
891 | }, | ||
892 | { | ||
893 | "authentication_method": 3, | ||
894 | "provider": "http://localhost:8089/" | ||
895 | } | ||
896 | ] | ||
897 | } | ||
898 | |||
899 | If the new policy is invalid, for example because it adds an unknown | ||
900 | authentication method, or the selected provider does not support the type of | ||
901 | authentication, the reducer will transition into an ``ERROR`` state instead of | ||
902 | modifying the policy. | ||
903 | |||
904 | |||
905 | |||
906 | **delete_policy:** | ||
907 | |||
908 | This transition allows the deletion of a recovery policy. The argument | ||
909 | simply specifies the index of the policy to delete, for example: | ||
910 | |||
911 | .. code-block:: json | ||
912 | |||
913 | { | ||
914 | "policy_index": 3 | ||
915 | } | ||
916 | |||
917 | Given as input the state from the example above, the expected new state would | ||
918 | be: | ||
919 | |||
920 | .. code-block:: json | ||
921 | |||
922 | { | ||
923 | "backup_state": "POLICIES_REVIEWING", | ||
924 | "policies": [ | ||
925 | { | ||
926 | "methods": [ | ||
927 | { | ||
928 | "authentication_method": 0, | ||
929 | "provider": "http://localhost:8089/" | ||
930 | }, | ||
931 | { | ||
932 | "authentication_method": 1, | ||
933 | "provider": "http://localhost:8088/" | ||
934 | } | ||
935 | ] | ||
936 | }, | ||
937 | { | ||
938 | "methods": [ | ||
939 | { | ||
940 | "authentication_method": 0, | ||
941 | "provider": "http://localhost:8089/" | ||
942 | }, | ||
943 | { | ||
944 | "authentication_method": 2, | ||
945 | "provider": "http://localhost:8088/" | ||
946 | } | ||
947 | ] | ||
948 | }, | ||
949 | { | ||
950 | "methods": [ | ||
951 | { | ||
952 | "authentication_method": 1, | ||
953 | "provider": "http://localhost:8089/" | ||
954 | }, | ||
955 | { | ||
956 | "authentication_method": 2, | ||
957 | "provider": "http://localhost:8088/" | ||
958 | } | ||
959 | ] | ||
960 | } | ||
961 | ] | ||
962 | } | ||
963 | |||
964 | If the index given is invalid, the reducer will transition into an ``ERROR`` state | ||
965 | instead of deleting a policy. | ||
966 | |||
967 | |||
968 | **delete_challenge:** | ||
969 | |||
970 | This transition allows the deletion of an individual | ||
971 | challenge from a recovery policy. The argument | ||
972 | simply specifies the index of the policy and challenge | ||
973 | to delete, for example: | ||
974 | |||
975 | .. code-block:: json | ||
976 | |||
977 | { | ||
978 | "policy_index": 1, | ||
979 | "challenge_index" : 1 | ||
980 | } | ||
981 | |||
982 | Given as input the state from the example above, the expected new state would | ||
983 | be: | ||
984 | |||
985 | .. code-block:: json | ||
986 | |||
987 | { | ||
988 | "backup_state": "POLICIES_REVIEWING", | ||
989 | "policies": [ | ||
990 | { | ||
991 | "methods": [ | ||
992 | { | ||
993 | "authentication_method": 0, | ||
994 | "provider": "http://localhost:8089/" | ||
995 | }, | ||
996 | { | ||
997 | "authentication_method": 1, | ||
998 | "provider": "http://localhost:8088/" | ||
999 | } | ||
1000 | ] | ||
1001 | }, | ||
1002 | { | ||
1003 | "methods": [ | ||
1004 | { | ||
1005 | "authentication_method": 0, | ||
1006 | "provider": "http://localhost:8089/" | ||
1007 | } | ||
1008 | ] | ||
1009 | }, | ||
1010 | { | ||
1011 | "methods": [ | ||
1012 | { | ||
1013 | "authentication_method": 1, | ||
1014 | "provider": "http://localhost:8089/" | ||
1015 | }, | ||
1016 | { | ||
1017 | "authentication_method": 2, | ||
1018 | "provider": "http://localhost:8088/" | ||
1019 | } | ||
1020 | ] | ||
1021 | } | ||
1022 | ] | ||
1023 | } | ||
1024 | |||
1025 | If the index given is invalid, the reducer will transition into an ``ERROR`` state | ||
1026 | instead of deleting a challenge. | ||
1027 | |||
1028 | |||
1029 | **next** (from ``POLICIES_REVIEWING``): | ||
1030 | |||
1031 | Using this transition, the user confirms that the policies in the current | ||
1032 | state are acceptable. The transition does not take any arguments. | ||
1033 | |||
1034 | The reducer will simply transition to the ``SECRET_EDITING`` state: | ||
1035 | |||
1036 | .. code-block:: json | ||
1037 | |||
1038 | { | ||
1039 | "backup_state": "SECRET_EDITING", | ||
1040 | "upload_fees" : [ "KUDOS:42" ], | ||
1041 | "expiration" : { "t_ms" : 1245362362 } | ||
1042 | } | ||
1043 | |||
1044 | Here, ``upload_fees`` is an array of applicable upload fees for the | ||
1045 | given policy expiration time. This is an array because fees could | ||
1046 | be in different currencies. The final cost may be lower if the | ||
1047 | user already paid for some of the time. | ||
1048 | |||
1049 | If the array of ``policies`` is currently empty, the reducer will transition | ||
1050 | into an ``ERROR`` state instead of allowing the user to continue. | ||
1051 | |||
1052 | |||
1053 | **enter_secret:** | ||
1054 | |||
1055 | This transition provides the reducer with the actual core ``secret`` of the user | ||
1056 | that Anastasis is supposed to backup (and possibly recover). The argument is | ||
1057 | simply the Crockford-Base32 encoded ``value`` together with its ``mime`` type, or a ``text`` field with a human-readable secret text. | ||
1058 | For example: | ||
1059 | |||
1060 | .. code-block:: javascript | ||
1061 | |||
1062 | { | ||
1063 | "secret": { | ||
1064 | "value": "EDJP6WK5EG50", | ||
1065 | "mime" : "text/plain" | ||
1066 | }, | ||
1067 | "expiration" : { "t_ms" : 1245362362 } | ||
1068 | } | ||
1069 | |||
1070 | If the application is unaware of the format, it set the ``mime`` field to ``null``. | ||
1071 | The ``expiration`` field is optional. | ||
1072 | |||
1073 | The reducer remains in the ``SECRET_EDITING`` state, but now the secret and | ||
1074 | updated expiration time are part of the state and the cost calculations will | ||
1075 | be updated. | ||
1076 | |||
1077 | .. code-block:: json | ||
1078 | |||
1079 | { | ||
1080 | "backup_state": "SECRET_EDITING", | ||
1081 | "core_secret" : { | ||
1082 | "value": "EDJP6WK5EG50", | ||
1083 | "mime" : "text/plain" | ||
1084 | }, | ||
1085 | "expiration" : { "t_ms" : 1245362362 }, | ||
1086 | "upload_fees" : [ "KUDOS:42" ] | ||
1087 | } | ||
1088 | |||
1089 | |||
1090 | **clear_secret:** | ||
1091 | |||
1092 | This transition removes the core secret from the state. It is simply a | ||
1093 | convenience function to undo ``enter_secret`` without providing a new value | ||
1094 | immediately. The transition takes no arguments. The resuting state will no | ||
1095 | longer have the ``core_secret`` field, and be otherwise unchanged. Calling | ||
1096 | **clear_secret** on a state without a ``core_secret`` will result in an error. | ||
1097 | |||
1098 | |||
1099 | **enter_secret_name:** | ||
1100 | |||
1101 | This transition provides the reducer with a name for the core ``secret`` of the user. This name will be given to the user as a hint when seleting a recovery policy document during recovery, prior to satisfying any of the challenges. The argument simply contains the name for the secret. | ||
1102 | Applications that have built-in support for Anastasis MUST prefix the | ||
1103 | secret name with an underscore and an application-specific identifier | ||
1104 | registered in GANA so that they can use recognize their own backups. | ||
1105 | An example argument would be: | ||
1106 | |||
1107 | .. code-block:: javascript | ||
1108 | |||
1109 | { | ||
1110 | "name": "_TALERWALLET_MyPinePhone", | ||
1111 | } | ||
1112 | |||
1113 | Here, ``MyPinePhone`` might be chosen by the user to identify the | ||
1114 | device that was being backed up. | ||
1115 | |||
1116 | The reducer remains in the ``SECRET_EDITING`` state, but now the | ||
1117 | secret name is updated: | ||
1118 | |||
1119 | .. code-block:: json | ||
1120 | |||
1121 | { | ||
1122 | "secret_name" : "_TALERWALLET_MyPinePhone" | ||
1123 | } | ||
1124 | |||
1125 | |||
1126 | **update_expiration:** | ||
1127 | |||
1128 | This transition asks the reducer to change the desired expiration time | ||
1129 | and to update the associated cost. For example: | ||
1130 | |||
1131 | .. code-block:: javascript | ||
1132 | |||
1133 | { | ||
1134 | "expiration" : { "t_ms" : 1245362362 } | ||
1135 | } | ||
1136 | |||
1137 | The reducer remains in the ``SECRET_EDITING`` state, but the | ||
1138 | expiration time and cost calculation will be updated. | ||
1139 | |||
1140 | .. code-block:: json | ||
1141 | |||
1142 | { | ||
1143 | "backup_state": "SECRET_EDITING", | ||
1144 | "expiration" : { "t_ms" : 1245362362 }, | ||
1145 | "upload_fees" : [ { "fee": "KUDOS:43" } ] | ||
1146 | } | ||
1147 | |||
1148 | |||
1149 | **next** (from ``SECRET_EDITING``): | ||
1150 | |||
1151 | Using this transition, the user confirms that the secret and expiration | ||
1152 | settings in the current state are acceptable. The transition does not take any | ||
1153 | arguments. | ||
1154 | |||
1155 | If the secret is currently empty, the reducer will transition into an | ||
1156 | ``ERROR`` state instead of allowing the user to continue. | ||
1157 | |||
1158 | After adding a secret, the reducer may transition into different states | ||
1159 | depending on whether payment(s) are necessary. If payments are needed, the | ||
1160 | ``secret`` will be stored in the state under ``core_secret``. Applications | ||
1161 | should be careful when persisting the resulting state, as the ``core_secret`` | ||
1162 | is not protected in the ``PAYING`` states. The ``PAYING`` states only differ | ||
1163 | in terms of what the payments are for (key shares or the recovery document), | ||
1164 | in all cases the state simply includes an array of Taler URIs that refer to | ||
1165 | payments that need to be made with the Taler wallet. | ||
1166 | |||
1167 | If all payments are complete, the reducer will transition into the | ||
1168 | ``BACKUP_FINISHED`` state and (if applicable) delete the ``core_secret`` as an | ||
1169 | additional safety measure. | ||
1170 | |||
1171 | Example results are thus: | ||
1172 | |||
1173 | .. code-block:: json | ||
1174 | |||
1175 | { | ||
1176 | "backup_state": "TRUTHS_PAYING", | ||
1177 | "secret_name" : "$NAME", | ||
1178 | "core_secret" : { "$anything":"$anything" }, | ||
1179 | "payments": [ | ||
1180 | "taler://pay/...", | ||
1181 | "taler://pay/..." | ||
1182 | ] | ||
1183 | } | ||
1184 | |||
1185 | .. code-block:: json | ||
1186 | |||
1187 | { | ||
1188 | "backup_state": "POLICIES_PAYING", | ||
1189 | "secret_name" : "$NAME", | ||
1190 | "core_secret" : { "$anything":"$anything" }, | ||
1191 | "payments": [ | ||
1192 | "taler://pay/...", | ||
1193 | "taler://pay/..." | ||
1194 | ] | ||
1195 | } | ||
1196 | |||
1197 | .. code-block:: json | ||
1198 | |||
1199 | { | ||
1200 | "backup_state": "BACKUP_FINISHED", | ||
1201 | "success_details": { | ||
1202 | "http://localhost:8080/" : { | ||
1203 | "policy_version" : 1, | ||
1204 | "policy_expiration" : { "t_ms" : 1245362362000 } | ||
1205 | }, | ||
1206 | "http://localhost:8081/" : { | ||
1207 | "policy_version" : 3, | ||
1208 | "policy_expiration" : { "t_ms" : 1245362362000 } | ||
1209 | } | ||
1210 | } | ||
1211 | } | ||
1212 | |||
1213 | |||
1214 | **pay:** | ||
1215 | |||
1216 | This transition suggests to the reducer that a payment may have been made or | ||
1217 | is immanent, and that the reducer should check with the Anastasis service | ||
1218 | provider to see if the operation is now possible. The operation takes one | ||
1219 | optional argument, which is a ``timeout`` value that specifies how long the | ||
1220 | reducer may wait (in long polling) for the payment to complete: | ||
1221 | |||
1222 | .. code-block:: json | ||
1223 | |||
1224 | { | ||
1225 | "timeout": { "d_ms" : 5000 }, | ||
1226 | } | ||
1227 | |||
1228 | The specified timeout is passed on to the Anastasis service provider(s), which | ||
1229 | will wait this long before giving up. If no timeout is given, the check is | ||
1230 | done as quickly as possible without additional delays. The reducer will continue | ||
1231 | to either an updated state with the remaining payment requests, to the | ||
1232 | ``BACKUP_FINISHED`` state (if all payments have been completed and the backup | ||
1233 | finished), or into an ``ERROR`` state in case there was an irrecoverable error, | ||
1234 | indicating the specific provider and how it failed. An example for this | ||
1235 | final error state would be: | ||
1236 | |||
1237 | .. code-block:: json | ||
1238 | |||
1239 | { | ||
1240 | "backup_state": "ERROR", | ||
1241 | "http_status" : 500, | ||
1242 | "upload_status" : 52, | ||
1243 | "provider_url" : "https://bad.example.com/", | ||
1244 | } | ||
1245 | |||
1246 | Here, the fields have the following meaning: | ||
1247 | |||
1248 | - **http_status** is the HTTP status returned by the Anastasis provider. | ||
1249 | - **upload_status** is the Taler error code return by the provider. | ||
1250 | - **provider_url** is the base URL of the failing provider. | ||
1251 | |||
1252 | In the above example, 52 would thus imply that the Anastasis provider failed to | ||
1253 | store information into its database. | ||
1254 | |||
1255 | |||
1256 | Recovery transitions | ||
1257 | -------------------- | ||
1258 | |||
1259 | **enter_user_attributes:** | ||
1260 | |||
1261 | This transition provides the user's personal attributes. The specific set of | ||
1262 | attributes required depends on the country of residence of the user. Some | ||
1263 | attributes may be optional, in which case they should be omitted entirely | ||
1264 | (that is, not simply be set to ``null`` or an empty string). The | ||
1265 | arguments are identical to the **enter_user_attributes** transition from | ||
1266 | the backup process. Example arguments would thus be: | ||
1267 | |||
1268 | .. code-block:: json | ||
1269 | |||
1270 | { | ||
1271 | "identity_attributes": { | ||
1272 | "full_name": "Max Musterman", | ||
1273 | "social_security_number": "123456789", | ||
1274 | "birthdate": "2000-01-01", | ||
1275 | "birthplace": "Earth" | ||
1276 | } | ||
1277 | } | ||
1278 | |||
1279 | However, in contrast to the backup process, the reducer will attempt to | ||
1280 | retrieve the latest recovery document from all known providers for the | ||
1281 | selected currency given the above inputs. If a recovery document was found | ||
1282 | by any provider, the reducer will attempt to load it and transition to | ||
1283 | a state where the user can choose which challenges to satisfy: | ||
1284 | |||
1285 | .. code-block:: json | ||
1286 | |||
1287 | { | ||
1288 | "recovery_state": "CHALLENGE_SELECTING", | ||
1289 | "recovery_information": { | ||
1290 | "challenges": [ | ||
1291 | { | ||
1292 | "uuid": "MW2R3RCBZPHNC78AW8AKWRCHF9KV3Y82EN62T831ZP54S3K5599G", | ||
1293 | "cost": "TESTKUDOS:0", | ||
1294 | "type": "question", | ||
1295 | "instructions": "q1" | ||
1296 | }, | ||
1297 | { | ||
1298 | "uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", | ||
1299 | "cost": "TESTKUDOS:0", | ||
1300 | "type": "email", | ||
1301 | "instructions": "e-mail address m?il@f*.bar" | ||
1302 | }, | ||
1303 | ], | ||
1304 | "policies": [ | ||
1305 | [ | ||
1306 | { | ||
1307 | "uuid": "MW2R3RCBZPHNC78AW8AKWRCHF9KV3Y82EN62T831ZP54S3K5599G" | ||
1308 | }, | ||
1309 | { | ||
1310 | "uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0" | ||
1311 | }, | ||
1312 | ], | ||
1313 | ], | ||
1314 | "provider_url": "http://localhost:8088/", | ||
1315 | "version": 1, | ||
1316 | }, | ||
1317 | "recovery_document": { | ||
1318 | "...": "..." | ||
1319 | } | ||
1320 | } | ||
1321 | |||
1322 | The ``recovery_document`` is an internal representation of the recovery | ||
1323 | information and of no concern to the user interface. The pertinent information | ||
1324 | is in the ``recovery_information``. Here, the ``challenges`` array is a list | ||
1325 | of possible challenges the user could attempt to solve next, while ``policies`` | ||
1326 | is an array of policies, with each policy being an array of challenges. | ||
1327 | Satisfying all of the challenges of one of the policies will enable the secret | ||
1328 | to be recovered. The ``provider_url`` from where the recovery document was | ||
1329 | obtained and its ``version`` are also provided. Each challenge comes with | ||
1330 | four mandatory fields: | ||
1331 | |||
1332 | - **uuid**: A unique identifier of the challenge; this is what the | ||
1333 | UUIDs in the policies array refer to, but also this UUID may be | ||
1334 | included in messages sent to the user. They allow the user to | ||
1335 | distinguish different PIN/TANs should say the same phone number be | ||
1336 | used for SMS-authentication with different providers. | ||
1337 | - **cost**: This is the amount the Anastasis provider will charge | ||
1338 | to allow the user to pass the challenge. | ||
1339 | - **type**: This is the type of the challenge, as a string. | ||
1340 | - **instructions**: Contains additional important hints for the user | ||
1341 | to allow the user to satisfy the challenge. It typically includes | ||
1342 | an abbreviated form of the contact information or the security | ||
1343 | question. Details depend on ``type``. | ||
1344 | |||
1345 | If a recovery document was not found, either the user never performed | ||
1346 | a backup, entered incorrect attributes, or used a provider not yet in | ||
1347 | the list of Anastasis providers. Hence, the user must now either | ||
1348 | select a different provider, or go ``back`` and update the identity | ||
1349 | attributes. In the case a recovery document was not found, the | ||
1350 | transition fails, returning the error code and a human-readable error | ||
1351 | message together with a transition failure: | ||
1352 | |||
1353 | .. code-block:: json | ||
1354 | |||
1355 | { | ||
1356 | "recovery_state": "ERROR", | ||
1357 | "error_message": "account unknown to Anastasis server", | ||
1358 | "error_code": 9, | ||
1359 | } | ||
1360 | |||
1361 | Here, the ``error_code`` is from the ``enum ANASTASIS_RecoveryStatus`` | ||
1362 | and describes precisely what failed about the download, while the | ||
1363 | ``error_message`` is a human-readable (English) explanation of the code. | ||
1364 | Applications may want to translate the message using GNU gettext; | ||
1365 | translations should be available in the ``anastasis`` text domain. | ||
1366 | However, in general it should be sufficient to display the slightly | ||
1367 | more generic Taler error code that is returned with the new state. | ||
1368 | |||
1369 | |||
1370 | **change_version:** | ||
1371 | |||
1372 | Even if a recovery document was found, it is possible that the user | ||
1373 | intended to recover a different version, or recover a backup where | ||
1374 | the recovery document is stored at a different provider. Thus, the | ||
1375 | reducer allows the user to explicitly switch to a different provider | ||
1376 | or recovery document version using the ``change_version`` transition, | ||
1377 | which takes a provider URL and policy version as arguments: | ||
1378 | |||
1379 | .. code-block:: json | ||
1380 | |||
1381 | { | ||
1382 | "provider_url": "https://localhost:8080/", | ||
1383 | "version": 2 | ||
1384 | } | ||
1385 | |||
1386 | Note that using a version of 0 implies fetching "the latest version". The | ||
1387 | resulting states are the same as those of the ``enter_user_attributes`` | ||
1388 | transition, except that the recovery document version is not necessarily the | ||
1389 | latest available version at the provider. | ||
1390 | |||
1391 | |||
1392 | **select_challenge:** | ||
1393 | |||
1394 | Selecting a challenge takes different, depending on the state of the payment. | ||
1395 | A comprehensive example for ``select_challenge`` would be: | ||
1396 | |||
1397 | .. code-block:: json | ||
1398 | |||
1399 | { | ||
1400 | "uuid": "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" | ||
1401 | "timeout" : { "d_ms" : 5000 }, | ||
1402 | "payment_secret": "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" | ||
1403 | } | ||
1404 | |||
1405 | The ``uuid`` field is mandatory and specifies the selected challenge. | ||
1406 | The other fields are optional, and are needed in case the user has | ||
1407 | previously been requested to pay for the challenge. In this case, | ||
1408 | the ``payment_secret`` identifies the previous payment request, and | ||
1409 | ``timeout`` says how long the Anastasis service should wait for the | ||
1410 | payment to be completed before giving up (long polling). | ||
1411 | |||
1412 | Depending on the type of the challenge and the need for payment, the | ||
1413 | reducer may transition into ``CHALLENGE_SOLVING`` or ``CHALLENGE_PAYING`` | ||
1414 | states. In ``CHALLENGE_SOLVING``, the new state will primarily specify | ||
1415 | the selected challenge: | ||
1416 | |||
1417 | .. code-block:: json | ||
1418 | |||
1419 | { | ||
1420 | "backup_state": "CHALLENGE_SOLVING", | ||
1421 | "selected_challenge_uuid": "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" | ||
1422 | } | ||
1423 | |||
1424 | In ``CHALLENGE_PAYING``, the new state will include instructions for payment | ||
1425 | in the ``challenge_feedback``. In general, ``challenge_feedback`` includes | ||
1426 | information about attempted challenges, with the final state being ``solved``: | ||
1427 | |||
1428 | .. code-block:: json | ||
1429 | |||
1430 | { | ||
1431 | "recovery_state": "CHALLENGE_SELECTING", | ||
1432 | "recovery_information": { | ||
1433 | "...": "..." | ||
1434 | } | ||
1435 | "challenge_feedback": { | ||
1436 | "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" : { | ||
1437 | "state" : "solved" | ||
1438 | } | ||
1439 | } | ||
1440 | } | ||
1441 | |||
1442 | Challenges feedback for a challenge can have many different ``state`` values | ||
1443 | that applications must all handle. States other than ``solved`` are: | ||
1444 | |||
1445 | - **payment**: Here, the user must pay for a challenge. An example would be: | ||
1446 | |||
1447 | .. code-block:: json | ||
1448 | |||
1449 | { | ||
1450 | "backup_state": "CHALLENGE_PAYING", | ||
1451 | "selected_challenge_uuid": "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30", | ||
1452 | "challenge_feedback": { | ||
1453 | "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" : { | ||
1454 | "state" : "payment", | ||
1455 | "taler_pay_uri" : "taler://pay/...", | ||
1456 | "provider" : "https://localhost:8080/", | ||
1457 | "payment_secret" : "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" | ||
1458 | } | ||
1459 | } | ||
1460 | } | ||
1461 | |||
1462 | - **body**: Here, the server provided an HTTP reply for | ||
1463 | how to solve the challenge, but the reducer could not parse | ||
1464 | them into a known format. A mime-type may be provided and may | ||
1465 | help parse the details. | ||
1466 | |||
1467 | .. code-block:: json | ||
1468 | |||
1469 | { | ||
1470 | "recovery_state": "CHALLENGE_SOLVING", | ||
1471 | "recovery_information": { | ||
1472 | "...": "..." | ||
1473 | } | ||
1474 | "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", | ||
1475 | "challenge_feedback": { | ||
1476 | "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { | ||
1477 | "state": "body", | ||
1478 | "body": "CROCKFORDBASE32ENCODEDBODY", | ||
1479 | "http_status": 403, | ||
1480 | "mime_type" : "anything/possible" | ||
1481 | } | ||
1482 | } | ||
1483 | } | ||
1484 | |||
1485 | - **hint**: Here, the server provided human-readable hint for | ||
1486 | how to solve the challenge. Note that the ``hint`` provided this | ||
1487 | time is from the Anastasis provider and may differ from the ``instructions`` | ||
1488 | for the challenge under ``recovery_information``: | ||
1489 | |||
1490 | .. code-block:: json | ||
1491 | |||
1492 | { | ||
1493 | "recovery_state": "CHALLENGE_SOLVING", | ||
1494 | "recovery_information": { | ||
1495 | "...": "..." | ||
1496 | } | ||
1497 | "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", | ||
1498 | "challenge_feedback": { | ||
1499 | "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { | ||
1500 | "state": "hint", | ||
1501 | "hint": "Recovery TAN send to email mail@DOMAIN", | ||
1502 | "http_status": 403 | ||
1503 | } | ||
1504 | } | ||
1505 | } | ||
1506 | |||
1507 | - **details**: Here, the server provided a detailed JSON status response | ||
1508 | related to solving the challenge: | ||
1509 | |||
1510 | .. code-block:: json | ||
1511 | |||
1512 | { | ||
1513 | "recovery_state": "CHALLENGE_SOLVING", | ||
1514 | "recovery_information": { | ||
1515 | "...": "..." | ||
1516 | } | ||
1517 | "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", | ||
1518 | "challenge_feedback": { | ||
1519 | "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { | ||
1520 | "state": "details", | ||
1521 | "details": { | ||
1522 | "code": 8111, | ||
1523 | "hint": "The client's response to the challenge was invalid.", | ||
1524 | "detail" : null | ||
1525 | }, | ||
1526 | "http_status": 403 | ||
1527 | } | ||
1528 | } | ||
1529 | } | ||
1530 | |||
1531 | - **redirect**: To solve the challenge, the user must visit the indicated | ||
1532 | Web site at ``redirect_url``, for example to perform video authentication: | ||
1533 | |||
1534 | .. code-block:: json | ||
1535 | |||
1536 | { | ||
1537 | "recovery_state": "CHALLENGE_SOLVING", | ||
1538 | "recovery_information": { | ||
1539 | "...": "..." | ||
1540 | } | ||
1541 | "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", | ||
1542 | "challenge_feedback": { | ||
1543 | "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { | ||
1544 | "state": "redirect", | ||
1545 | "redirect_url": "https://videoconf.example.com/", | ||
1546 | "http_status": 303 | ||
1547 | } | ||
1548 | } | ||
1549 | } | ||
1550 | |||
1551 | - **server-failure**: This indicates that the Anastasis provider encountered | ||
1552 | a failure and recovery using this challenge cannot proceed at this time. | ||
1553 | Examples for failures might be that the provider is unable to send SMS | ||
1554 | messages at this time due to an outage. The body includes details about | ||
1555 | the failure. The user may try again later or continue with other challenges. | ||
1556 | |||
1557 | .. code-block:: json | ||
1558 | |||
1559 | { | ||
1560 | "recovery_state": "CHALLENGE_SELECTING", | ||
1561 | "recovery_information": { | ||
1562 | "...": "..." | ||
1563 | } | ||
1564 | "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", | ||
1565 | "challenge_feedback": { | ||
1566 | "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { | ||
1567 | "state": "server-failure", | ||
1568 | "http_status": "500", | ||
1569 | "error_code": 52 | ||
1570 | } | ||
1571 | } | ||
1572 | } | ||
1573 | |||
1574 | - **truth-unknown**: This indicates that the Anastasis provider is unaware of | ||
1575 | the specified challenge. This is typically a permanent failure, and user | ||
1576 | interfaces should not allow users to re-try this challenge. | ||
1577 | |||
1578 | .. code-block:: json | ||
1579 | |||
1580 | { | ||
1581 | "recovery_state": "CHALLENGE_SELECTING", | ||
1582 | "recovery_information": { | ||
1583 | "...": "..." | ||
1584 | } | ||
1585 | "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", | ||
1586 | "challenge_feedback": { | ||
1587 | "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { | ||
1588 | "state": "truth-unknown", | ||
1589 | "error_code": 8108 | ||
1590 | } | ||
1591 | } | ||
1592 | } | ||
1593 | |||
1594 | - **rate-limit-exceeded**: | ||
1595 | |||
1596 | .. code-block:: json | ||
1597 | |||
1598 | { | ||
1599 | "recovery_state": "CHALLENGE_SELECTING", | ||
1600 | "recovery_information": { | ||
1601 | "...": "..." | ||
1602 | } | ||
1603 | "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", | ||
1604 | "challenge_feedback": { | ||
1605 | "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { | ||
1606 | "state": "rate-limit-exceeded", | ||
1607 | "error_code": 8121 | ||
1608 | } | ||
1609 | } | ||
1610 | } | ||
1611 | |||
1612 | **pay:** | ||
1613 | |||
1614 | With a ``pay`` transition, the application indicates to the reducer that | ||
1615 | a payment may have been made. Here, it is again possible to specify an | ||
1616 | optional ``timeout`` argument for long-polling, for example: | ||
1617 | |||
1618 | .. code-block:: json | ||
1619 | |||
1620 | { | ||
1621 | "payment_secret": "ABCDADF242525AABASD52525235ABABFDABABANALASDAAKASDAS" | ||
1622 | "timeout" : { "d_ms" : 5000 }, | ||
1623 | } | ||
1624 | |||
1625 | Depending on the type of the challenge and the result of the operation, the | ||
1626 | new state may be ``CHALLENGE_SOLVING`` (if say the SMS was now sent to the | ||
1627 | user), ``CHALLENGE_SELECTING`` (if the answer to the security question was | ||
1628 | correct), ``RECOVERY_FINISHED`` (if this was the last challenge that needed to | ||
1629 | be solved) or still ``CHALLENGE_PAYING`` (if the challenge was not actually | ||
1630 | paid for). For sample messages, see the different types of | ||
1631 | ``challenge_feedback`` in the section about ``select_challenge``. | ||
1632 | |||
1633 | |||
1634 | **solve_challenge:** | ||
1635 | |||
1636 | Solving a challenge takes various formats, depending on the type of the | ||
1637 | challenge and what is known about the answer. The different supported | ||
1638 | formats are: | ||
1639 | |||
1640 | .. code-block:: json | ||
1641 | |||
1642 | { | ||
1643 | "answer": "answer to security question" | ||
1644 | } | ||
1645 | |||
1646 | .. code-block:: json | ||
1647 | |||
1648 | { | ||
1649 | "pin": 1234 | ||
1650 | } | ||
1651 | |||
1652 | .. code-block:: json | ||
1653 | |||
1654 | { | ||
1655 | "hash": "SOMEBASE32ENCODEDHASHVALUE" | ||
1656 | } | ||