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