reducer.rst (57674B)
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. This is an array because fees could 1061 be in different currencies. The final cost may be lower if the 1062 user already paid for some of the time. 1063 1064 If the array of ``policies`` is currently empty, the reducer will 1065 return an error response instead of allowing the user to continue. 1066 1067 1068 **enter_secret:** 1069 1070 This transition provides the reducer with the actual core ``secret`` of the user 1071 that Anastasis is supposed to backup (and possibly recover). The argument is 1072 simply the Crockford-Base32 encoded ``value`` together with its ``mime`` type, or a ``text`` field with a human-readable secret text. 1073 For example: 1074 1075 .. code-block:: javascript 1076 1077 { 1078 "secret": { 1079 "value": "EDJP6WK5EG50", 1080 "mime" : "text/plain" 1081 }, 1082 "expiration" : { "t_ms" : 1245362362 } 1083 } 1084 1085 If the application is unaware of the format, it set the ``mime`` field to ``null``. 1086 The ``expiration`` field is optional. 1087 1088 The reducer remains in the ``SECRET_EDITING`` state, but now the secret and 1089 updated expiration time are part of the state and the cost calculations will 1090 be updated. 1091 1092 .. code-block:: json 1093 1094 { 1095 "backup_state": "SECRET_EDITING", 1096 "core_secret" : { 1097 "value": "EDJP6WK5EG50", 1098 "mime" : "text/plain" 1099 }, 1100 "expiration" : { "t_ms" : 1245362362 }, 1101 "upload_fees" : [ { "fee": "KUDOS:42" } ] 1102 } 1103 1104 1105 **clear_secret:** 1106 1107 This transition removes the core secret from the state. It is simply a 1108 convenience function to undo ``enter_secret`` without providing a new value 1109 immediately. The transition takes no arguments. The resuting state will no 1110 longer have the ``core_secret`` field, and be otherwise unchanged. Calling 1111 **clear_secret** on a state without a ``core_secret`` will result in an error. 1112 1113 1114 **enter_secret_name:** 1115 1116 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. 1117 Applications that have built-in support for Anastasis MUST prefix the 1118 secret name with an underscore and an application-specific identifier 1119 registered in GANA so that they can use recognize their own backups. 1120 An example argument would be: 1121 1122 .. code-block:: javascript 1123 1124 { 1125 "name": "_TALERWALLET_MyPinePhone", 1126 } 1127 1128 Here, ``MyPinePhone`` might be chosen by the user to identify the 1129 device that was being backed up. 1130 1131 The reducer remains in the ``SECRET_EDITING`` state, but now the 1132 secret name is updated: 1133 1134 .. code-block:: json 1135 1136 { 1137 "secret_name" : "_TALERWALLET_MyPinePhone" 1138 } 1139 1140 1141 **update_expiration:** 1142 1143 This transition asks the reducer to change the desired expiration time 1144 and to update the associated cost. For example: 1145 1146 .. code-block:: javascript 1147 1148 { 1149 "expiration" : { "t_ms" : 1245362362 } 1150 } 1151 1152 The reducer remains in the ``SECRET_EDITING`` state, but the 1153 expiration time and cost calculation will be updated. 1154 1155 .. code-block:: json 1156 1157 { 1158 "backup_state": "SECRET_EDITING", 1159 "expiration" : { "t_ms" : 1245362362 }, 1160 "upload_fees" : [ { "fee": "KUDOS:43" } ] 1161 } 1162 1163 1164 **next** (from ``SECRET_EDITING``): 1165 1166 Using this transition, the user confirms that the secret and expiration 1167 settings in the current state are acceptable. The transition does not take any 1168 arguments. 1169 1170 If the secret is currently empty, the reducer will return an 1171 error response instead of allowing the user to continue. 1172 1173 After adding a secret, the reducer may transition into different states 1174 depending on whether payment(s) are necessary. If payments are needed, the 1175 ``secret`` will be stored in the state under ``core_secret``. Applications 1176 should be careful when persisting the resulting state, as the ``core_secret`` 1177 is not protected in the ``PAYING`` states. The ``PAYING`` states only differ 1178 in terms of what the payments are for (key shares or the recovery document), 1179 in all cases the state simply includes an array of Taler URIs that refer to 1180 payments that need to be made with the Taler wallet. 1181 1182 If all payments are complete, the reducer will transition into the 1183 ``BACKUP_FINISHED`` state and (if applicable) delete the ``core_secret`` as an 1184 additional safety measure. 1185 1186 Example results are thus: 1187 1188 .. code-block:: json 1189 1190 { 1191 "backup_state": "TRUTHS_PAYING", 1192 "secret_name" : "$NAME", 1193 "core_secret" : { "$anything":"$anything" }, 1194 "payments": [ 1195 "taler://pay/...", 1196 "taler://pay/..." 1197 ] 1198 } 1199 1200 .. code-block:: json 1201 1202 { 1203 "backup_state": "POLICIES_PAYING", 1204 "secret_name" : "$NAME", 1205 "core_secret" : { "$anything":"$anything" }, 1206 "payments": [ 1207 "taler://pay/...", 1208 "taler://pay/..." 1209 ] 1210 } 1211 1212 .. code-block:: json 1213 1214 { 1215 "backup_state": "BACKUP_FINISHED", 1216 "success_details": { 1217 "http://localhost:8080/" : { 1218 "policy_version" : 1, 1219 "policy_expiration" : { "t_ms" : 1245362362000 } 1220 }, 1221 "http://localhost:8081/" : { 1222 "policy_version" : 3, 1223 "policy_expiration" : { "t_ms" : 1245362362000 } 1224 } 1225 } 1226 } 1227 1228 1229 **pay:** 1230 1231 This transition suggests to the reducer that a payment may have been made or 1232 is immanent, and that the reducer should check with the Anastasis service 1233 provider to see if the operation is now possible. The operation takes one 1234 optional argument, which is a ``timeout`` value that specifies how long the 1235 reducer may wait (in long polling) for the payment to complete: 1236 1237 .. code-block:: json 1238 1239 { 1240 "timeout": { "d_ms" : 5000 }, 1241 } 1242 1243 The specified timeout is passed on to the Anastasis service provider(s), which 1244 will wait this long before giving up. If no timeout is given, the check is 1245 done as quickly as possible without additional delays. The reducer will continue 1246 to either an updated state with the remaining payment requests, to the 1247 ``BACKUP_FINISHED`` state (if all payments have been completed and the backup 1248 finished), or return an error response in case there was an irrecoverable error, 1249 indicating the specific provider and how it failed. An example for this 1250 final error state would be: 1251 1252 .. code-block:: json 1253 1254 { 1255 "http_status" : 500, 1256 "upload_status" : 52, 1257 "provider_url" : "https://bad.example.com/", 1258 } 1259 1260 Here, the fields have the following meaning: 1261 1262 - **http_status** is the HTTP status returned by the Anastasis provider. 1263 - **upload_status** is the Taler error code return by the provider. 1264 - **provider_url** is the base URL of the failing provider. 1265 1266 In the above example, 52 would thus imply that the Anastasis provider failed to 1267 store information into its database. 1268 1269 1270 Recovery transitions 1271 -------------------- 1272 1273 **enter_user_attributes:** 1274 1275 This transition provides the user's personal attributes. The specific set of 1276 attributes required depends on the country of residence of the user. Some 1277 attributes may be optional, in which case they should be omitted entirely 1278 (that is, not simply be set to ``null`` or an empty string). The 1279 arguments are identical to the **enter_user_attributes** transition from 1280 the backup process. Example arguments would thus be: 1281 1282 .. code-block:: json 1283 1284 { 1285 "identity_attributes": { 1286 "full_name": "Max Musterman", 1287 "social_security_number": "123456789", 1288 "birthdate": "2000-01-01", 1289 "birthplace": "Earth" 1290 } 1291 } 1292 1293 Afterwards, the reducer transitions into the ``SECRET_SELECTING`` state: 1294 1295 .. code-block:: json 1296 1297 { 1298 "recovery_state": "SECRET_SELECTING", 1299 "identity_attributes": { 1300 "full_name": "Max Musterman", 1301 "social_security_number": "123456789", 1302 "birthdate": "2000-01-01", 1303 "birthplace": "Earth" 1304 } 1305 } 1306 1307 Typically, the special policy discovery process (outside of the state 1308 machine) is expected to be run in this state. The discovery process 1309 will use the state (and in particular the identity attributes and the 1310 list of active providers) to discover a set of possible recovery 1311 documents with their respective provider URLs, policy version and 1312 identity attribute mask. An identity attribute mask is a bitmask that 1313 describes which of the optional attributes from the identity 1314 attributes should be omitted to recover this backup. Once the user 1315 has selected a backup providing this triplet, it is possible to 1316 proceed using ``next``. 1317 1318 Especially if the discovered policies are inadequate, it is again 1319 possible to add providers using ``add_provider``. 1320 1321 1322 **add_provider**: 1323 1324 This operation can be performed in state ``SECRET_SELECTING``. It 1325 adds one additional Anastasis provider to the list of providers that 1326 the discovery process should henceforth consider. Note that removing 1327 providers is not possible at this time. 1328 1329 Here, the client must provide an object with the base URL of the 1330 providers to add, for example: 1331 1332 .. code-block:: json 1333 1334 { 1335 "provider_url" : "http://localhost:8088/" 1336 } 1337 1338 1339 **select_version**: 1340 1341 Using the ``select_version`` transition in the ``SECRET_SELECTING`` state, 1342 it is possible to trigger the download and decryption of a recovery 1343 policy document. Here, the arguments specify which provider, version 1344 and mask should be used to download the document: 1345 1346 .. code-block:: json 1347 1348 { 1349 "providers" : [ { 1350 "url": "https://localhost:8088/", 1351 "version": 0 1352 } ], 1353 "attribute_mask": 0 1354 } 1355 1356 The reducer will attempt to retrieve the specified recovery document 1357 from that provider. If a recovery document was found, the reducer 1358 will attempt to load it and transition to a state where the user can 1359 choose which challenges to satisfy: 1360 1361 .. code-block:: json 1362 1363 { 1364 "recovery_state": "CHALLENGE_SELECTING", 1365 "recovery_information": { 1366 "challenges": [ 1367 { 1368 "uuid": "MW2R3RCBZPHNC78AW8AKWRCHF9KV3Y82EN62T831ZP54S3K5599G", 1369 "uuid-display": "MW2R3RC", 1370 "type": "question", 1371 "instructions": "q1" 1372 }, 1373 { 1374 "uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1375 "uuid-display": "TXYKGE", 1376 "type": "email", 1377 "instructions": "e-mail address m?il@f*.bar" 1378 }, 1379 ], 1380 "policies": [ 1381 [ 1382 { 1383 "uuid": "MW2R3RCBZPHNC78AW8AKWRCHF9KV3Y82EN62T831ZP54S3K5599G" 1384 }, 1385 { 1386 "uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0" 1387 }, 1388 ], 1389 ], 1390 "provider_url": "http://localhost:8088/", 1391 "version": 1, 1392 }, 1393 "recovery_document": { 1394 "...": "..." 1395 } 1396 } 1397 1398 The ``recovery_document`` is an internal representation of the recovery 1399 information and of no concern to the user interface. The pertinent information 1400 is in the ``recovery_information``. Here, the ``challenges`` array is a list 1401 of possible challenges the user could attempt to solve next, while ``policies`` 1402 is an array of policies, with each policy being an array of challenges. 1403 Satisfying all of the challenges of one of the policies will enable the secret 1404 to be recovered. The ``provider_url`` from where the recovery document was 1405 obtained and its ``version`` are also provided. Each challenge comes with 1406 four mandatory fields: 1407 1408 - **uuid**: A unique identifier of the challenge; this is what the 1409 UUIDs in the policies array refer to. 1410 - **uuid-display**: Shortened idenfier which is included in messages 1411 send to the user. Allows the user to 1412 distinguish different PIN/TANs should say the same phone number be 1413 used for SMS-authentication with different providers. 1414 - **type**: This is the type of the challenge, as a string. 1415 - **instructions**: Contains additional important hints for the user 1416 to allow the user to satisfy the challenge. It typically includes 1417 an abbreviated form of the contact information or the security 1418 question. Details depend on ``type``. 1419 1420 If a recovery document was not found, either the user never performed 1421 a backup, entered incorrect attributes, or used a provider not yet in 1422 the list of Anastasis providers. Hence, the user must now either 1423 select a different provider, or go ``back`` and update the identity 1424 attributes. In the case a recovery document was not found, the 1425 transition fails, returning the error code and a human-readable error 1426 message together with a transition failure: 1427 1428 .. code-block:: json 1429 1430 { 1431 "error_message": "account unknown to Anastasis server", 1432 "error_code": 9, 1433 } 1434 1435 Here, the ``error_code`` is from the ``enum ANASTASIS_RecoveryStatus`` 1436 and describes precisely what failed about the download, while the 1437 ``error_message`` is a human-readable (English) explanation of the code. 1438 Applications may want to translate the message using GNU gettext; 1439 translations should be available in the ``anastasis`` text domain. 1440 However, in general it should be sufficient to display the slightly 1441 more generic Taler error code that is returned with the new state. 1442 1443 1444 **sync_providers** 1445 1446 The downloaded policy may include secrets from providers for which 1447 we do not (yet) have the cost structure or even the salt. So here 1448 an application can use the ``sync_providers`` request to download 1449 ``/config`` from providers that are in the challenge list but not 1450 yet known with their salt and other attributes in the provider list. 1451 1452 The transition fails if all providers relevant for the selected 1453 policy are already downloaded. Applications may either internally 1454 check the state for this, or call ``sync_providers`` until it fails 1455 with this error: 1456 1457 .. code-block:: json 1458 1459 { 1460 "detail": "already in sync", 1461 "code": 8400, 1462 "hint": "The given action is invalid for the current state of the reducer." 1463 } 1464 1465 As providers may fail to respond, this action may need to be called 1466 repeatedly. The action will block until progress is made on any provider. 1467 As some providers may never respond, the application should disable 1468 challenge buttons for challenges where providers are down. However, 1469 users should be able to solve challenges where the provider is up while 1470 the reducer is polling for ``/config`` in the background. 1471 1472 1473 **select_challenge:** 1474 1475 Selecting a challenge takes different, depending on the state of the payment. 1476 A comprehensive example for ``select_challenge`` would be: 1477 1478 .. code-block:: json 1479 1480 { 1481 "uuid": "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" 1482 "timeout" : { "d_ms" : 5000 }, 1483 "payment_secret": "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" 1484 } 1485 1486 The ``uuid`` field is mandatory and specifies the selected challenge. 1487 The other fields are optional, and are needed in case the user has 1488 previously been requested to pay for the challenge. In this case, 1489 the ``payment_secret`` identifies the previous payment request, and 1490 ``timeout`` says how long the Anastasis service should wait for the 1491 payment to be completed before giving up (long polling). 1492 1493 Depending on the type of the challenge and the need for payment, the 1494 reducer may transition into ``CHALLENGE_SOLVING`` or ``CHALLENGE_PAYING`` 1495 states. In ``CHALLENGE_SOLVING``, the new state will primarily specify 1496 the selected challenge: 1497 1498 .. code-block:: json 1499 1500 { 1501 "backup_state": "CHALLENGE_SOLVING", 1502 "selected_challenge_uuid": "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" 1503 } 1504 1505 In ``CHALLENGE_PAYING``, the new state will include instructions for payment 1506 in the ``challenge_feedback``. In general, ``challenge_feedback`` includes 1507 information about attempted challenges, with the final state being ``solved``: 1508 1509 .. code-block:: json 1510 1511 { 1512 "recovery_state": "CHALLENGE_SELECTING", 1513 "recovery_information": { 1514 "...": "..." 1515 } 1516 "challenge_feedback": { 1517 "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" : { 1518 "state" : "solved" 1519 } 1520 } 1521 } 1522 1523 Challenges feedback for a challenge can have many different ``state`` values 1524 that applications must all handle. States other than ``solved`` are: 1525 1526 - **payment**: Here, the user must pay for a challenge. An example would be: 1527 1528 .. code-block:: json 1529 1530 { 1531 "backup_state": "CHALLENGE_PAYING", 1532 "selected_challenge_uuid": "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30", 1533 "challenge_feedback": { 1534 "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" : { 1535 "state" : "payment", 1536 "taler_pay_uri" : "taler://pay/...", 1537 "provider" : "https://localhost:8080/", 1538 "payment_secret" : "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" 1539 } 1540 } 1541 } 1542 1543 - **body**: Here, the server provided an HTTP reply for 1544 how to solve the challenge, but the reducer could not parse 1545 them into a known format. A mime-type may be provided and may 1546 help parse the details. 1547 1548 .. code-block:: json 1549 1550 { 1551 "recovery_state": "CHALLENGE_SOLVING", 1552 "recovery_information": { 1553 "...": "..." 1554 } 1555 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1556 "challenge_feedback": { 1557 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1558 "state": "body", 1559 "body": "CROCKFORDBASE32ENCODEDBODY", 1560 "http_status": 403, 1561 "mime_type" : "anything/possible" 1562 } 1563 } 1564 } 1565 1566 - **hint**: Here, the server provided human-readable hint for 1567 how to solve the challenge. Note that the ``hint`` provided this 1568 time is from the Anastasis provider and may differ from the ``instructions`` 1569 for the challenge under ``recovery_information``: 1570 1571 .. code-block:: json 1572 1573 { 1574 "recovery_state": "CHALLENGE_SOLVING", 1575 "recovery_information": { 1576 "...": "..." 1577 } 1578 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1579 "challenge_feedback": { 1580 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1581 "state": "hint", 1582 "hint": "Recovery TAN send to email mail@DOMAIN", 1583 "http_status": 403 1584 } 1585 } 1586 } 1587 1588 - **details**: Here, the server provided a detailed JSON status response 1589 related to solving the challenge: 1590 1591 .. code-block:: json 1592 1593 { 1594 "recovery_state": "CHALLENGE_SOLVING", 1595 "recovery_information": { 1596 "...": "..." 1597 } 1598 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1599 "challenge_feedback": { 1600 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1601 "state": "details", 1602 "details": { 1603 "code": 8111, 1604 "hint": "The client's response to the challenge was invalid.", 1605 "detail" : null 1606 }, 1607 "http_status": 403 1608 } 1609 } 1610 } 1611 1612 - **redirect**: To solve the challenge, the user must visit the indicated 1613 Web site at ``redirect_url``, for example to perform video authentication: 1614 1615 .. code-block:: json 1616 1617 { 1618 "recovery_state": "CHALLENGE_SOLVING", 1619 "recovery_information": { 1620 "...": "..." 1621 } 1622 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1623 "challenge_feedback": { 1624 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1625 "state": "redirect", 1626 "redirect_url": "https://videoconf.example.com/", 1627 "http_status": 303 1628 } 1629 } 1630 } 1631 1632 - **server-failure**: This indicates that the Anastasis provider encountered 1633 a failure and recovery using this challenge cannot proceed at this time. 1634 Examples for failures might be that the provider is unable to send SMS 1635 messages at this time due to an outage. The body includes details about 1636 the failure. The user may try again later or continue with other challenges. 1637 1638 .. code-block:: json 1639 1640 { 1641 "recovery_state": "CHALLENGE_SELECTING", 1642 "recovery_information": { 1643 "...": "..." 1644 } 1645 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1646 "challenge_feedback": { 1647 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1648 "state": "server-failure", 1649 "http_status": "500", 1650 "error_code": 52 1651 } 1652 } 1653 } 1654 1655 - **truth-unknown**: This indicates that the Anastasis provider is unaware of 1656 the specified challenge. This is typically a permanent failure, and user 1657 interfaces should not allow users to re-try this challenge. 1658 1659 .. code-block:: json 1660 1661 { 1662 "recovery_state": "CHALLENGE_SELECTING", 1663 "recovery_information": { 1664 "...": "..." 1665 } 1666 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1667 "challenge_feedback": { 1668 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1669 "state": "truth-unknown", 1670 "error_code": 8108 1671 } 1672 } 1673 } 1674 1675 - **rate-limit-exceeded**: This indicates that the user has made too many invalid attempts in too short an amount of time. 1676 1677 .. code-block:: json 1678 1679 { 1680 "recovery_state": "CHALLENGE_SELECTING", 1681 "recovery_information": { 1682 "...": "..." 1683 } 1684 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1685 "challenge_feedback": { 1686 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1687 "state": "rate-limit-exceeded", 1688 "error_code": 8121 1689 } 1690 } 1691 } 1692 1693 - **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. 1694 1695 .. code-block:: json 1696 1697 { 1698 "recovery_state": "CHALLENGE_SELECTING", 1699 "recovery_information": { 1700 "...": "..." 1701 } 1702 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1703 "challenge_feedback": { 1704 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1705 "state": "authentication-timeout", 1706 "error_code": 8122 1707 } 1708 } 1709 } 1710 1711 - **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. 1712 1713 .. code-block:: json 1714 1715 { 1716 "recovery_state": "CHALLENGE_SELECTING", 1717 "recovery_information": { 1718 "...": "..." 1719 } 1720 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1721 "challenge_feedback": { 1722 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1723 "state": "external-instructions", 1724 "method": "iban", 1725 "async": true, // optional 1726 "answer_code": 987654321, // optional 1727 "details": { 1728 "...": "..." 1729 } 1730 } 1731 } 1732 } 1733 1734 If "async" is "true", then the client should 1735 poll for the challenge being satisfied using 1736 the "answer_code" that has been provided. 1737 1738 The specific instructions on how to satisfy 1739 the challenge depend on the ``method``. 1740 They include: 1741 1742 - **iban**: The user must perform a wire transfer from their account to the Anastasis provider. 1743 1744 .. code-block:: json 1745 1746 { 1747 "challenge_amount": "EUR:1", 1748 "credit_iban": "DE12345789000", 1749 "business_name": "Data Loss Incorporated", 1750 "wire_transfer_subject": "Anastasis 987654321" 1751 } 1752 1753 Note that the actual wire transfer subject must contain both 1754 the numeric ``answer_code`` as well as 1755 the string ``Anastasis``. 1756 1757 **poll:** 1758 1759 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: 1760 1761 .. code-block:: json 1762 1763 { 1764 "timeout" : { "d_ms" : 5000 }, 1765 } 1766 1767 1768 **pay:** 1769 1770 With a ``pay`` transition, the application indicates to the reducer that 1771 a payment may have been made. Here, it is again possible to specify an 1772 optional ``timeout`` argument for long-polling, for example: 1773 1774 .. code-block:: json 1775 1776 { 1777 "payment_secret": "ABCDADF242525AABASD52525235ABABFDABABANALASDAAKASDAS" 1778 "timeout" : { "d_ms" : 5000 }, 1779 } 1780 1781 Depending on the type of the challenge and the result of the operation, the 1782 new state may be ``CHALLENGE_SOLVING`` (if say the SMS was now sent to the 1783 user), ``CHALLENGE_SELECTING`` (if the answer to the security question was 1784 correct), ``RECOVERY_FINISHED`` (if this was the last challenge that needed to 1785 be solved) or still ``CHALLENGE_PAYING`` (if the challenge was not actually 1786 paid for). For sample messages, see the different types of 1787 ``challenge_feedback`` in the section about ``select_challenge``. 1788 1789 1790 **solve_challenge:** 1791 1792 Solving a challenge takes various formats, depending on the type of the 1793 challenge and what is known about the answer. The different supported 1794 formats are: 1795 1796 .. code-block:: json 1797 1798 { 1799 "answer": "answer to security question" 1800 } 1801 1802 .. code-block:: json 1803 1804 { 1805 "pin": 1234 1806 } 1807 1808 .. code-block:: json 1809 1810 { 1811 "hash": "SOMEBASE32ENCODEDHASHVALUE" 1812 }