reducer.rst (57878B)
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 "truth_lifetime": { "d_ms" : 50000000 }, 413 "storage_limit_in_megabytes": 1, 414 "provider_name": "Anastasis 4", 415 "salt": "CXAPCKSH9D3MYJTS9536RHJHCW" 416 }, 417 "http://localhost:8088/": { 418 "http_status": 200, 419 "methods": [ 420 { "type" : "question", 421 "usage_fee" : "EUR:0.01" }, 422 { "type" : "sms", 423 "usage_fee" : "EUR:0.55" } 424 ], 425 "annual_fee": "EUR:0.99", 426 "truth_upload_fee": "EUR:3.99", 427 "liability_limit": "EUR:1", 428 "currency": "EUR", 429 "truth_lifetime": { "d_ms" : 50000000 }, 430 "storage_limit_in_megabytes": 1, 431 "provider_name": "Anastasis 4", 432 "salt": "CXAPCKSH9D3MYJTS9536RHJHCW" 433 } 434 } 435 } 436 437 The array of ``required_attributes`` contains attributes about the user 438 that must be provided includes: 439 440 - **type**: The type of the attribute, for now only ``string`` and ``date`` are 441 supported. 442 - **name**: The name of the attribute, this is the key under which the 443 attribute value must be provided later. The name must be unique per response. 444 - **label**: A human-readable description of the attribute in English. 445 Translated descriptions may be provided under **label_i18n**. 446 - **uuid**: A UUID that uniquely identifies identical attributes across 447 different countries. Useful to preserve values should the user enter 448 some attributes, and then switch to another country. Note that 449 attributes must not be preserved if they merely have the same **name**, 450 only the **uuid** will be identical if the semantics is identical. 451 - **widget**: An optional name of a widget that is known to nicely render 452 the attribute entry in user interfaces where named widgets are 453 supported. 454 - **validation-regex**: An optional extended POSIX regular expression 455 that is to be used to validate (string) inputs to ensure they are 456 well-formed. 457 - **validation-logic**: Optional name of a function that should be called 458 to validate the input. If the function is not known to the particular 459 client, the respective validation can be skipped (at the expense of 460 typos by users not being detected, possibly rendering secrets 461 irrecoverable). 462 - **optional**: Optional boolean field that, if ``true``, indicates that 463 this attribute is not actually required but optional and users MAY leave 464 it blank in case they do not have the requested information. Used for 465 common fields that apply to some large part of the population but are 466 not sufficiently universal to be actually required. 467 468 The authentication providers are listed under a key that is the 469 base URL of the service. For each provider, the following 470 information is provided if the provider was successfully contacted: 471 472 - **http_status**: HTTP status code, always ``200`` on success. 473 - **methods**: Array of authentication methods supported by this 474 provider. Includes the **type** of the authentication method 475 and the **usage_fee** (how much the user must pay for authorization 476 using this method during recovery). 477 - **annual_fee**: Fee the provider charges to store the recovery 478 policy for one year. 479 - **truth_upload_fee**: Fee the provider charges to store a key share. 480 - **liability_limit**: Amount the provider can be held liable for in 481 case a key share or recovery document cannot be recovered due to 482 provider failures. 483 - **currency**: Currency in which the provider wants to be paid, 484 will match all of the fees. 485 - **storage_limit_in_megabytes**: Maximum size of an upload (for 486 both recovery document and truth data) in megabytes. 487 - **provider_name**: Human-readable name of the provider's business. 488 - **salt**: Salt value used by the provider, used to derive the 489 user's identity at this provider. Should be unique per provider, 490 and must never change for a given provider. The salt is 491 base32 encoded. 492 493 If contacting the provider failed, the information returned is: 494 495 - **http_status**: HTTP status code (if available, possibly 0 if 496 we did not even obtain an HTTP response). 497 - **error_code**: Taler error code, never 0. 498 499 500 **add_provider**: 501 502 This operation can be performed in state ``USER_ATTRIBUTES_COLLECTING``. 503 It 504 adds one or more Anastasis providers to the list of providers the reducer 505 should henceforth consider. Note that removing providers is not possible at 506 this time. 507 508 Here, the client must provide an object with the base URLs of the 509 providers to add or disable. The object maps the 510 URLs to status information about the provider to 511 use. For example: 512 513 .. code-block:: json 514 515 { 516 "http://localhost:8088/" : { "disabled" : false }, 517 "http://localhost:8089/" : { "disabled" : false }, 518 "http://localhost:8090/" : { "disabled" : true } 519 } 520 521 Note that existing providers will remain in the state they were in. The following is an 522 example for an expected new state where the service on port 8089 is 523 unreachable, the services on port 8088 and 8888 were previously known, and service on 524 port 8088 was now added, and on 8090 is disabled: 525 526 .. code-block:: json 527 528 { 529 "backup_state": "USER_ATTRIBUTES_COLLECTING", 530 "authentication_providers": { 531 "http://localhost:8089/": { 532 "disabled": false, 533 "error_code": 11, 534 "http_status": 0 535 }, 536 "http://localhost:8090/": { 537 "disabled": true 538 }, 539 "http://localhost:8088/": { 540 "disabled": false, 541 "http_status": 200, 542 "methods": [ 543 { "type" : "question", 544 "usage_fee" : "EUR:0.01" }, 545 { "type" : "sms", 546 "usage_fee" : "EUR:0.55" } 547 ], 548 "annual_fee": "EUR:0.99", 549 "truth_upload_fee": "EUR:3.99", 550 "liability_limit": "EUR:1", 551 "currency": "EUR", 552 "truth_lifetime": { "d_ms" : 50000000 }, 553 "storage_limit_in_megabytes": 1, 554 "provider_name": "Anastasis 4", 555 "salt": "CXAPCKSH9D3MYJTS9536RHJHCW" 556 } 557 "http://localhost:8888/": { 558 "methods": [ 559 { "type" : "question", 560 "usage_fee" : "EUR:0.01" }, 561 { "type" : "sms", 562 "usage_fee" : "EUR:0.55" } 563 ], 564 "annual_fee": "EUR:0.99", 565 "truth_upload_fee": "EUR:3.99", 566 "liability_limit": "EUR:1", 567 "currency": "EUR", 568 "truth_lifetime": { "d_ms" : 50000000 }, 569 "storage_limit_in_megabytes": 1, 570 "provider_name": "Anastasis 42", 571 "salt": "BXAPCKSH9D3MYJTS9536RHJHCX" 572 } 573 } 574 } 575 576 577 578 Backup transitions 579 ------------------ 580 581 **enter_user_attributes:** 582 583 This transition provides the user's personal attributes. The specific set of 584 attributes required depends on the country of residence of the user. Some 585 attributes may be optional, in which case they should be omitted entirely 586 (that is, not simply be set to ``null`` or an empty string). Example 587 arguments would be: 588 589 .. code-block:: json 590 591 { 592 "identity_attributes": { 593 "full_name": "Max Musterman", 594 "social_security_number": "123456789", 595 "birthdate": "2000-01-01", 596 "birthplace": "Earth" 597 } 598 } 599 600 Note that at this stage, the state machines between backup and 601 recovery diverge and the ``recovery_state`` will begin to look 602 very different from the ``backup_state``. 603 604 For backups, if all required attributes are present, the reducer will 605 transition to an ``AUTHENTICATIONS_EDITING`` state with the attributes added 606 to it: 607 608 .. code-block:: json 609 610 { 611 "backup_state": "AUTHENTICATIONS_EDITING", 612 "identity_attributes": { 613 "full_name": "Max Musterman", 614 "social_security_number": "123456789", 615 "birthdate": "2000-01-01", 616 "birthplace": "Earth" 617 } 618 } 619 620 If required attributes are missing, do not match the required regular 621 expression, or fail the custom validation logic, the reducer SHOULD return an 622 error response indicating that the transition has failed and what is wrong about 623 the input and not transition to a new state. A reducer that does not support 624 some specific validation logic MAY accept the invalid input and proceed anyway. 625 The error state will include a Taler error code that is specific to the 626 failure, and optional details. 627 628 Example: 629 630 .. code-block:: json 631 632 { 633 "code": 8404, 634 "hint": "An input did not match the regular expression.", 635 "detail": "social_security_number" 636 } 637 638 Clients may safely repeat this transition to validate the user's inputs 639 until they satisfy all of the constraints. This way, the user interface 640 does not have to perform the input validation directly. 641 642 643 **add_authentication**: 644 645 This transition adds an authentication method. The method must be supported 646 by one or more providers that are included in the current state. Adding an 647 authentication method requires specifying the ``type`` and ``instructions`` to 648 be given to the user. The ``challenge`` is encrypted and stored at the 649 Anastasis provider. The specific semantics of the value depend on the 650 ``type``. Typical challenges values are a phone number (to send an SMS to), 651 an e-mail address (to send a PIN code to) or the answer to a security 652 question. Note that these challenge values will still be encrypted (and 653 possibly hashed) before being given to the Anastasis providers. 654 655 Note that the ``challenge`` must be given in Crockford Base32 encoding, as it 656 MAY include binary data (such as a photograph of the user). In the latter 657 case, the optional ``mime_type`` field must be provided to give the MIME type 658 of the value encoded in ``challenge``. 659 660 .. code-block:: json 661 662 { 663 "authentication_method": 664 { 665 "type": "question", 666 "mime_type" : "text/plain", 667 "instructions" : "What is your favorite GNU package?", 668 "challenge" : "E1QPPS8A", 669 } 670 } 671 672 If the information provided is valid, the reducer will add the new 673 authentication method to the array of authentication methods: 674 675 .. code-block:: json 676 677 { 678 "backup_state": "AUTHENTICATIONS_EDITING", 679 "authentication_methods": [ 680 { 681 "type": "question", 682 "mime_type" : "text/plain", 683 "instructions" : "What is your favorite GNU package?", 684 "challenge" : "E1QPPS8A", 685 }, 686 { 687 "type": "email", 688 "instructions" : "E-mail to user@*le.com", 689 "challenge": "ENSPAWJ0CNW62VBGDHJJWRVFDM50" 690 } 691 ] 692 } 693 694 695 **delete_authentication**: 696 697 This transition can be used to remove an authentication method from the 698 array of authentication methods. It simply requires the index of the 699 authentication method to remove. Note that the array is 0-indexed: 700 701 .. code-block:: json 702 703 { 704 "authentication_method": 1 705 } 706 707 Assuming we begin with the state from the example above, this would 708 remove the ``email`` authentication method, resulting in the following 709 response: 710 711 .. code-block:: json 712 713 { 714 "backup_state": "AUTHENTICATIONS_EDITING", 715 "authentication_methods": [ 716 { 717 "type": "question", 718 "mime_type" : "text/plain", 719 "instructions" : "What is your favorite GNU package?", 720 "challenge" : "gdb", 721 } 722 ] 723 } 724 725 If the index is invalid, the reducer will return an error 726 response instead of making a transition. 727 728 729 **next** (from ``AUTHENTICATIONS_EDITING``): 730 731 This transition confirms that the user has finished adding (or removing) 732 authentication methods, and that the system should now automatically compute 733 a set of reasonable recovery policies. 734 735 This transition does not take any mandatory arguments. Optional arguments can 736 be provided to upload the recovery document only to a specific subset of the 737 providers: 738 739 .. code-block:: json 740 741 { 742 "providers": [ 743 "http://localhost:8088/", 744 "http://localhost:8089/" 745 ] 746 } 747 748 The resulting state provides the suggested recovery policies in a way suitable 749 for presentation to the user: 750 751 .. code-block:: javascript 752 753 { 754 "backup_state": "POLICIES_REVIEWING", 755 "policy_providers" : [ 756 { "provider_url" : "http://localhost:8088/" }, 757 { "provider_url" : "http://localhost:8089/" } 758 ], 759 "policies": [ 760 { 761 "methods": [ 762 { 763 "authentication_method": 0, 764 "provider": "http://localhost:8088/" 765 }, 766 { 767 "authentication_method": 1, 768 "provider": "http://localhost:8089/" 769 }, 770 { 771 "authentication_method": 2, 772 "provider": "http://localhost:8087/" 773 } 774 ] 775 }, 776 { 777 "methods": [ 778 { 779 "authentication_method": 0, 780 "provider": "http://localhost:8088/" 781 }, 782 { 783 "authentication_method": 1, 784 "provider": "http://localhost:8089/" 785 }, 786 { 787 "authentication_method": 3, 788 "provider": "http://localhost:8089/" 789 } 790 ] 791 } 792 ] 793 } 794 795 For each recovery policy, the state includes the specific details of which 796 authentication ``methods`` must be solved to recovery the secret using this 797 policy. The ``methods`` array specifies the index of the 798 ``authentication_method`` in the ``authentication_methods`` array, as well as 799 the provider that was selected to supervise this authentication. 800 801 If no authentication method was provided, the reducer will 802 return an error response instead of making a transition. 803 804 805 **add_policy**: 806 807 Using this transition, the user can add an additional recovery policy to the 808 state. The argument format is the same that is used in the existing state. 809 An example for a possible argument would thus be: 810 811 .. code-block:: javascript 812 813 { 814 "policy": [ 815 { 816 "authentication_method": 1, 817 "provider": "http://localhost:8088/" 818 }, 819 { 820 "authentication_method": 3, 821 "provider": "http://localhost:8089/" 822 } 823 ] 824 } 825 826 Note that the specified providers must already be in the 827 ``authentication_providers`` of the state. You cannot add new providers at 828 this stage. The reducer will simply attempt to append the suggested policy to 829 the "policies" array, returning an updated state: 830 831 .. code-block:: json 832 833 { 834 "backup_state": "POLICIES_REVIEWING", 835 "policies": [ 836 { 837 "methods": [ 838 { 839 "authentication_method": 0, 840 "provider": "http://localhost:8089/" 841 }, 842 { 843 "authentication_method": 1, 844 "provider": "http://localhost:8088/" 845 } 846 ] 847 }, 848 { 849 "methods": [ 850 { 851 "authentication_method": 0, 852 "provider": "http://localhost:8089/" 853 }, 854 { 855 "authentication_method": 2, 856 "provider": "http://localhost:8088/" 857 } 858 ] 859 }, 860 { 861 "methods": [ 862 { 863 "authentication_method": 1, 864 "provider": "http://localhost:8089/" 865 }, 866 { 867 "authentication_method": 2, 868 "provider": "http://localhost:8088/" 869 } 870 ] 871 }, 872 { 873 "methods": [ 874 { 875 "authentication_method": 1, 876 "provider": "http://localhost:8088/" 877 }, 878 { 879 "authentication_method": 3, 880 "provider": "http://localhost:8089/" 881 } 882 ] 883 } 884 ] 885 } 886 887 If the new policy is invalid, for example because it adds an unknown 888 authentication method, or the selected provider does not support the type of 889 authentication, the reducer return an error response instead of 890 adding the new policy. 891 892 893 **update_policy**: 894 895 Using this transition, the user can modify an existing recovery policy 896 in the state. 897 The argument format is the same that is used in **add_policy**, 898 except there is an additional key ``policy_index`` which 899 identifies the policy to modify. 900 An example for a possible argument would thus be: 901 902 .. code-block:: javascript 903 904 { 905 "policy_index" : 1, 906 "policy": [ 907 { 908 "authentication_method": 1, 909 "provider": "http://localhost:8088/" 910 }, 911 { 912 "authentication_method": 3, 913 "provider": "http://localhost:8089/" 914 } 915 ] 916 } 917 918 If the new policy is invalid, for example because it adds an unknown 919 authentication method, or the selected provider does not support the type of 920 authentication, the reducer will return an error response instead of 921 modifying the policy. 922 923 924 925 **delete_policy:** 926 927 This transition allows the deletion of a recovery policy. The argument 928 simply specifies the index of the policy to delete, for example: 929 930 .. code-block:: json 931 932 { 933 "policy_index": 3 934 } 935 936 Given as input the state from the example above, the expected new state would 937 be: 938 939 .. code-block:: json 940 941 { 942 "backup_state": "POLICIES_REVIEWING", 943 "policies": [ 944 { 945 "methods": [ 946 { 947 "authentication_method": 0, 948 "provider": "http://localhost:8089/" 949 }, 950 { 951 "authentication_method": 1, 952 "provider": "http://localhost:8088/" 953 } 954 ] 955 }, 956 { 957 "methods": [ 958 { 959 "authentication_method": 0, 960 "provider": "http://localhost:8089/" 961 }, 962 { 963 "authentication_method": 2, 964 "provider": "http://localhost:8088/" 965 } 966 ] 967 }, 968 { 969 "methods": [ 970 { 971 "authentication_method": 1, 972 "provider": "http://localhost:8089/" 973 }, 974 { 975 "authentication_method": 2, 976 "provider": "http://localhost:8088/" 977 } 978 ] 979 } 980 ] 981 } 982 983 If the index given is invalid, the reducer will return an error response 984 instead of deleting a policy. 985 986 987 **delete_challenge:** 988 989 This transition allows the deletion of an individual 990 challenge from a recovery policy. The argument 991 simply specifies the index of the policy and challenge 992 to delete, for example: 993 994 .. code-block:: json 995 996 { 997 "policy_index": 1, 998 "challenge_index" : 1 999 } 1000 1001 Given as input the state from the example above, the expected new state would 1002 be: 1003 1004 .. code-block:: json 1005 1006 { 1007 "backup_state": "POLICIES_REVIEWING", 1008 "policies": [ 1009 { 1010 "methods": [ 1011 { 1012 "authentication_method": 0, 1013 "provider": "http://localhost:8089/" 1014 }, 1015 { 1016 "authentication_method": 1, 1017 "provider": "http://localhost:8088/" 1018 } 1019 ] 1020 }, 1021 { 1022 "methods": [ 1023 { 1024 "authentication_method": 0, 1025 "provider": "http://localhost:8089/" 1026 } 1027 ] 1028 }, 1029 { 1030 "methods": [ 1031 { 1032 "authentication_method": 1, 1033 "provider": "http://localhost:8089/" 1034 }, 1035 { 1036 "authentication_method": 2, 1037 "provider": "http://localhost:8088/" 1038 } 1039 ] 1040 } 1041 ] 1042 } 1043 1044 If the index given is invalid, the reducer will return an error response 1045 instead of deleting a challenge. 1046 1047 1048 **next** (from ``POLICIES_REVIEWING``): 1049 1050 Using this transition, the user confirms that the policies in the current 1051 state are acceptable. The transition does not take any arguments. 1052 1053 The reducer will simply transition to the ``SECRET_EDITING`` state: 1054 1055 .. code-block:: json 1056 1057 { 1058 "backup_state": "SECRET_EDITING", 1059 "upload_fees" : [ { "fee": "KUDOS:42" } ], 1060 "expiration" : { "t_ms" : 1245362362 } 1061 } 1062 1063 Here, ``upload_fees`` is an array of applicable upload fees for the 1064 given policy expiration time. This is an array because fees could 1065 be in different currencies. The final cost may be lower if the 1066 user already paid for some of the time. 1067 1068 If the array of ``policies`` is currently empty, the reducer will 1069 return an error response instead of allowing the user to continue. 1070 1071 1072 **enter_secret:** 1073 1074 This transition provides the reducer with the actual core ``secret`` of the user 1075 that Anastasis is supposed to backup (and possibly recover). The argument is 1076 simply the Crockford-Base32 encoded ``value`` together with its ``mime`` type, or a ``text`` field with a human-readable secret text. 1077 For example: 1078 1079 .. code-block:: javascript 1080 1081 { 1082 "secret": { 1083 "value": "EDJP6WK5EG50", 1084 "mime" : "text/plain" 1085 }, 1086 "expiration" : { "t_ms" : 1245362362 } 1087 } 1088 1089 If the application is unaware of the format, it set the ``mime`` field to ``null``. 1090 The ``expiration`` field is optional. 1091 1092 The reducer remains in the ``SECRET_EDITING`` state, but now the secret and 1093 updated expiration time are part of the state and the cost calculations will 1094 be updated. 1095 1096 .. code-block:: json 1097 1098 { 1099 "backup_state": "SECRET_EDITING", 1100 "core_secret" : { 1101 "value": "EDJP6WK5EG50", 1102 "mime" : "text/plain" 1103 }, 1104 "expiration" : { "t_ms" : 1245362362 }, 1105 "upload_fees" : [ { "fee": "KUDOS:42" } ] 1106 } 1107 1108 1109 **clear_secret:** 1110 1111 This transition removes the core secret from the state. It is simply a 1112 convenience function to undo ``enter_secret`` without providing a new value 1113 immediately. The transition takes no arguments. The resuting state will no 1114 longer have the ``core_secret`` field, and be otherwise unchanged. Calling 1115 **clear_secret** on a state without a ``core_secret`` will result in an error. 1116 1117 1118 **enter_secret_name:** 1119 1120 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. 1121 Applications that have built-in support for Anastasis MUST prefix the 1122 secret name with an underscore and an application-specific identifier 1123 registered in GANA so that they can use recognize their own backups. 1124 An example argument would be: 1125 1126 .. code-block:: javascript 1127 1128 { 1129 "name": "_TALERWALLET_MyPinePhone", 1130 } 1131 1132 Here, ``MyPinePhone`` might be chosen by the user to identify the 1133 device that was being backed up. 1134 1135 The reducer remains in the ``SECRET_EDITING`` state, but now the 1136 secret name is updated: 1137 1138 .. code-block:: json 1139 1140 { 1141 "secret_name" : "_TALERWALLET_MyPinePhone" 1142 } 1143 1144 1145 **update_expiration:** 1146 1147 This transition asks the reducer to change the desired expiration time 1148 and to update the associated cost. For example: 1149 1150 .. code-block:: javascript 1151 1152 { 1153 "expiration" : { "t_ms" : 1245362362 } 1154 } 1155 1156 The reducer remains in the ``SECRET_EDITING`` state, but the 1157 expiration time and cost calculation will be updated. 1158 1159 .. code-block:: json 1160 1161 { 1162 "backup_state": "SECRET_EDITING", 1163 "expiration" : { "t_ms" : 1245362362 }, 1164 "upload_fees" : [ { "fee": "KUDOS:43" } ] 1165 } 1166 1167 1168 **next** (from ``SECRET_EDITING``): 1169 1170 Using this transition, the user confirms that the secret and expiration 1171 settings in the current state are acceptable. The transition does not take any 1172 arguments. 1173 1174 If the secret is currently empty, the reducer will return an 1175 error response instead of allowing the user to continue. 1176 1177 After adding a secret, the reducer may transition into different states 1178 depending on whether payment(s) are necessary. If payments are needed, the 1179 ``secret`` will be stored in the state under ``core_secret``. Applications 1180 should be careful when persisting the resulting state, as the ``core_secret`` 1181 is not protected in the ``PAYING`` states. The ``PAYING`` states only differ 1182 in terms of what the payments are for (key shares or the recovery document), 1183 in all cases the state simply includes an array of Taler URIs that refer to 1184 payments that need to be made with the Taler wallet. 1185 1186 If all payments are complete, the reducer will transition into the 1187 ``BACKUP_FINISHED`` state and (if applicable) delete the ``core_secret`` as an 1188 additional safety measure. 1189 1190 Example results are thus: 1191 1192 .. code-block:: json 1193 1194 { 1195 "backup_state": "TRUTHS_PAYING", 1196 "secret_name" : "$NAME", 1197 "core_secret" : { "$anything":"$anything" }, 1198 "payments": [ 1199 "taler://pay/...", 1200 "taler://pay/..." 1201 ] 1202 } 1203 1204 .. code-block:: json 1205 1206 { 1207 "backup_state": "POLICIES_PAYING", 1208 "secret_name" : "$NAME", 1209 "core_secret" : { "$anything":"$anything" }, 1210 "payments": [ 1211 "taler://pay/...", 1212 "taler://pay/..." 1213 ] 1214 } 1215 1216 .. code-block:: json 1217 1218 { 1219 "backup_state": "BACKUP_FINISHED", 1220 "success_details": { 1221 "http://localhost:8080/" : { 1222 "policy_version" : 1, 1223 "policy_expiration" : { "t_ms" : 1245362362000 } 1224 }, 1225 "http://localhost:8081/" : { 1226 "policy_version" : 3, 1227 "policy_expiration" : { "t_ms" : 1245362362000 } 1228 } 1229 } 1230 } 1231 1232 1233 **pay:** 1234 1235 This transition suggests to the reducer that a payment may have been made or 1236 is immanent, and that the reducer should check with the Anastasis service 1237 provider to see if the operation is now possible. The operation takes one 1238 optional argument, which is a ``timeout`` value that specifies how long the 1239 reducer may wait (in long polling) for the payment to complete: 1240 1241 .. code-block:: json 1242 1243 { 1244 "timeout": { "d_ms" : 5000 }, 1245 } 1246 1247 The specified timeout is passed on to the Anastasis service provider(s), which 1248 will wait this long before giving up. If no timeout is given, the check is 1249 done as quickly as possible without additional delays. The reducer will continue 1250 to either an updated state with the remaining payment requests, to the 1251 ``BACKUP_FINISHED`` state (if all payments have been completed and the backup 1252 finished), or return an error response in case there was an irrecoverable error, 1253 indicating the specific provider and how it failed. An example for this 1254 final error state would be: 1255 1256 .. code-block:: json 1257 1258 { 1259 "http_status" : 500, 1260 "upload_status" : 52, 1261 "provider_url" : "https://bad.example.com/", 1262 } 1263 1264 Here, the fields have the following meaning: 1265 1266 - **http_status** is the HTTP status returned by the Anastasis provider. 1267 - **upload_status** is the Taler error code return by the provider. 1268 - **provider_url** is the base URL of the failing provider. 1269 1270 In the above example, 52 would thus imply that the Anastasis provider failed to 1271 store information into its database. 1272 1273 1274 Recovery transitions 1275 -------------------- 1276 1277 **enter_user_attributes:** 1278 1279 This transition provides the user's personal attributes. The specific set of 1280 attributes required depends on the country of residence of the user. Some 1281 attributes may be optional, in which case they should be omitted entirely 1282 (that is, not simply be set to ``null`` or an empty string). The 1283 arguments are identical to the **enter_user_attributes** transition from 1284 the backup process. Example arguments would thus be: 1285 1286 .. code-block:: json 1287 1288 { 1289 "identity_attributes": { 1290 "full_name": "Max Musterman", 1291 "social_security_number": "123456789", 1292 "birthdate": "2000-01-01", 1293 "birthplace": "Earth" 1294 } 1295 } 1296 1297 Afterwards, the reducer transitions into the ``SECRET_SELECTING`` state: 1298 1299 .. code-block:: json 1300 1301 { 1302 "recovery_state": "SECRET_SELECTING", 1303 "identity_attributes": { 1304 "full_name": "Max Musterman", 1305 "social_security_number": "123456789", 1306 "birthdate": "2000-01-01", 1307 "birthplace": "Earth" 1308 } 1309 } 1310 1311 Typically, the special policy discovery process (outside of the state 1312 machine) is expected to be run in this state. The discovery process 1313 will use the state (and in particular the identity attributes and the 1314 list of active providers) to discover a set of possible recovery 1315 documents with their respective provider URLs, policy version and 1316 identity attribute mask. An identity attribute mask is a bitmask that 1317 describes which of the optional attributes from the identity 1318 attributes should be omitted to recover this backup. Once the user 1319 has selected a backup providing this triplet, it is possible to 1320 proceed using ``next``. 1321 1322 Especially if the discovered policies are inadequate, it is again 1323 possible to add providers using ``add_provider``. 1324 1325 1326 **add_provider**: 1327 1328 This operation can be performed in state ``SECRET_SELECTING``. It 1329 adds one additional Anastasis provider to the list of providers that 1330 the discovery process should henceforth consider. Note that removing 1331 providers is not possible at this time. 1332 1333 Here, the client must provide an object with the base URL of the 1334 providers to add, for example: 1335 1336 .. code-block:: json 1337 1338 { 1339 "provider_url" : "http://localhost:8088/" 1340 } 1341 1342 1343 **select_version**: 1344 1345 Using the ``select_version`` transition in the ``SECRET_SELECTING`` state, 1346 it is possible to trigger the download and decryption of a recovery 1347 policy document. Here, the arguments specify which provider, version 1348 and mask should be used to download the document: 1349 1350 .. code-block:: json 1351 1352 { 1353 "providers" : [ { 1354 "url": "https://localhost:8088/", 1355 "version": 0 1356 } ], 1357 "attribute_mask": 0 1358 } 1359 1360 The reducer will attempt to retrieve the specified recovery document 1361 from that provider. If a recovery document was found, the reducer 1362 will attempt to load it and transition to a state where the user can 1363 choose which challenges to satisfy: 1364 1365 .. code-block:: json 1366 1367 { 1368 "recovery_state": "CHALLENGE_SELECTING", 1369 "recovery_information": { 1370 "challenges": [ 1371 { 1372 "uuid": "MW2R3RCBZPHNC78AW8AKWRCHF9KV3Y82EN62T831ZP54S3K5599G", 1373 "uuid-display": "MW2R3RC", 1374 "type": "question", 1375 "instructions": "q1" 1376 }, 1377 { 1378 "uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1379 "uuid-display": "TXYKGE", 1380 "type": "email", 1381 "instructions": "e-mail address m?il@f*.bar" 1382 }, 1383 ], 1384 "policies": [ 1385 [ 1386 { 1387 "uuid": "MW2R3RCBZPHNC78AW8AKWRCHF9KV3Y82EN62T831ZP54S3K5599G" 1388 }, 1389 { 1390 "uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0" 1391 }, 1392 ], 1393 ], 1394 "provider_url": "http://localhost:8088/", 1395 "version": 1, 1396 }, 1397 "recovery_document": { 1398 "...": "..." 1399 } 1400 } 1401 1402 The ``recovery_document`` is an internal representation of the recovery 1403 information and of no concern to the user interface. The pertinent information 1404 is in the ``recovery_information``. Here, the ``challenges`` array is a list 1405 of possible challenges the user could attempt to solve next, while ``policies`` 1406 is an array of policies, with each policy being an array of challenges. 1407 Satisfying all of the challenges of one of the policies will enable the secret 1408 to be recovered. The ``provider_url`` from where the recovery document was 1409 obtained and its ``version`` are also provided. Each challenge comes with 1410 four mandatory fields: 1411 1412 - **uuid**: A unique identifier of the challenge; this is what the 1413 UUIDs in the policies array refer to. 1414 - **uuid-display**: Shortened idenfier which is included in messages 1415 send to the user. Allows the user to 1416 distinguish different PIN/TANs should say the same phone number be 1417 used for SMS-authentication with different providers. 1418 - **type**: This is the type of the challenge, as a string. 1419 - **instructions**: Contains additional important hints for the user 1420 to allow the user to satisfy the challenge. It typically includes 1421 an abbreviated form of the contact information or the security 1422 question. Details depend on ``type``. 1423 1424 If a recovery document was not found, either the user never performed 1425 a backup, entered incorrect attributes, or used a provider not yet in 1426 the list of Anastasis providers. Hence, the user must now either 1427 select a different provider, or go ``back`` and update the identity 1428 attributes. In the case a recovery document was not found, the 1429 transition fails, returning the error code and a human-readable error 1430 message together with a transition failure: 1431 1432 .. code-block:: json 1433 1434 { 1435 "error_message": "account unknown to Anastasis server", 1436 "error_code": 9, 1437 } 1438 1439 Here, the ``error_code`` is from the ``enum ANASTASIS_RecoveryStatus`` 1440 and describes precisely what failed about the download, while the 1441 ``error_message`` is a human-readable (English) explanation of the code. 1442 Applications may want to translate the message using GNU gettext; 1443 translations should be available in the ``anastasis`` text domain. 1444 However, in general it should be sufficient to display the slightly 1445 more generic Taler error code that is returned with the new state. 1446 1447 1448 **sync_providers** 1449 1450 The downloaded policy may include secrets from providers for which 1451 we do not (yet) have the cost structure or even the salt. So here 1452 an application can use the ``sync_providers`` request to download 1453 ``/config`` from providers that are in the challenge list but not 1454 yet known with their salt and other attributes in the provider list. 1455 1456 The transition fails if all providers relevant for the selected 1457 policy are already downloaded. Applications may either internally 1458 check the state for this, or call ``sync_providers`` until it fails 1459 with this error: 1460 1461 .. code-block:: json 1462 1463 { 1464 "detail": "already in sync", 1465 "code": 8400, 1466 "hint": "The given action is invalid for the current state of the reducer." 1467 } 1468 1469 As providers may fail to respond, this action may need to be called 1470 repeatedly. The action will block until progress is made on any provider. 1471 As some providers may never respond, the application should disable 1472 challenge buttons for challenges where providers are down. However, 1473 users should be able to solve challenges where the provider is up while 1474 the reducer is polling for ``/config`` in the background. 1475 1476 1477 **select_challenge:** 1478 1479 Selecting a challenge takes different, depending on the state of the payment. 1480 A comprehensive example for ``select_challenge`` would be: 1481 1482 .. code-block:: json 1483 1484 { 1485 "uuid": "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" 1486 "timeout" : { "d_ms" : 5000 }, 1487 "payment_secret": "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" 1488 } 1489 1490 The ``uuid`` field is mandatory and specifies the selected challenge. 1491 The other fields are optional, and are needed in case the user has 1492 previously been requested to pay for the challenge. In this case, 1493 the ``payment_secret`` identifies the previous payment request, and 1494 ``timeout`` says how long the Anastasis service should wait for the 1495 payment to be completed before giving up (long polling). 1496 1497 Depending on the type of the challenge and the need for payment, the 1498 reducer may transition into ``CHALLENGE_SOLVING`` or ``CHALLENGE_PAYING`` 1499 states. In ``CHALLENGE_SOLVING``, the new state will primarily specify 1500 the selected challenge: 1501 1502 .. code-block:: json 1503 1504 { 1505 "backup_state": "CHALLENGE_SOLVING", 1506 "selected_challenge_uuid": "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" 1507 } 1508 1509 In ``CHALLENGE_PAYING``, the new state will include instructions for payment 1510 in the ``challenge_feedback``. In general, ``challenge_feedback`` includes 1511 information about attempted challenges, with the final state being ``solved``: 1512 1513 .. code-block:: json 1514 1515 { 1516 "recovery_state": "CHALLENGE_SELECTING", 1517 "recovery_information": { 1518 "...": "..." 1519 } 1520 "challenge_feedback": { 1521 "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" : { 1522 "state" : "solved" 1523 } 1524 } 1525 } 1526 1527 Challenges feedback for a challenge can have many different ``state`` values 1528 that applications must all handle. States other than ``solved`` are: 1529 1530 - **payment**: Here, the user must pay for a challenge. An example would be: 1531 1532 .. code-block:: json 1533 1534 { 1535 "backup_state": "CHALLENGE_PAYING", 1536 "selected_challenge_uuid": "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30", 1537 "challenge_feedback": { 1538 "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" : { 1539 "state" : "payment", 1540 "taler_pay_uri" : "taler://pay/...", 1541 "provider" : "https://localhost:8080/", 1542 "payment_secret" : "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" 1543 } 1544 } 1545 } 1546 1547 - **body**: Here, the server provided an HTTP reply for 1548 how to solve the challenge, but the reducer could not parse 1549 them into a known format. A mime-type may be provided and may 1550 help parse the details. 1551 1552 .. code-block:: json 1553 1554 { 1555 "recovery_state": "CHALLENGE_SOLVING", 1556 "recovery_information": { 1557 "...": "..." 1558 } 1559 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1560 "challenge_feedback": { 1561 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1562 "state": "body", 1563 "body": "CROCKFORDBASE32ENCODEDBODY", 1564 "http_status": 403, 1565 "mime_type" : "anything/possible" 1566 } 1567 } 1568 } 1569 1570 - **hint**: Here, the server provided human-readable hint for 1571 how to solve the challenge. Note that the ``hint`` provided this 1572 time is from the Anastasis provider and may differ from the ``instructions`` 1573 for the challenge under ``recovery_information``: 1574 1575 .. code-block:: json 1576 1577 { 1578 "recovery_state": "CHALLENGE_SOLVING", 1579 "recovery_information": { 1580 "...": "..." 1581 } 1582 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1583 "challenge_feedback": { 1584 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1585 "state": "hint", 1586 "hint": "Recovery TAN send to email mail@DOMAIN", 1587 "http_status": 403 1588 } 1589 } 1590 } 1591 1592 - **details**: Here, the server provided a detailed JSON status response 1593 related to solving the challenge: 1594 1595 .. code-block:: json 1596 1597 { 1598 "recovery_state": "CHALLENGE_SOLVING", 1599 "recovery_information": { 1600 "...": "..." 1601 } 1602 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1603 "challenge_feedback": { 1604 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1605 "state": "details", 1606 "details": { 1607 "code": 8111, 1608 "hint": "The client's response to the challenge was invalid.", 1609 "detail" : null 1610 }, 1611 "http_status": 403 1612 } 1613 } 1614 } 1615 1616 - **redirect**: To solve the challenge, the user must visit the indicated 1617 Web site at ``redirect_url``, for example to perform video authentication: 1618 1619 .. code-block:: json 1620 1621 { 1622 "recovery_state": "CHALLENGE_SOLVING", 1623 "recovery_information": { 1624 "...": "..." 1625 } 1626 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1627 "challenge_feedback": { 1628 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1629 "state": "redirect", 1630 "redirect_url": "https://videoconf.example.com/", 1631 "http_status": 303 1632 } 1633 } 1634 } 1635 1636 - **server-failure**: This indicates that the Anastasis provider encountered 1637 a failure and recovery using this challenge cannot proceed at this time. 1638 Examples for failures might be that the provider is unable to send SMS 1639 messages at this time due to an outage. The body includes details about 1640 the failure. The user may try again later or continue with other challenges. 1641 1642 .. code-block:: json 1643 1644 { 1645 "recovery_state": "CHALLENGE_SELECTING", 1646 "recovery_information": { 1647 "...": "..." 1648 } 1649 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1650 "challenge_feedback": { 1651 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1652 "state": "server-failure", 1653 "http_status": "500", 1654 "error_code": 52 1655 } 1656 } 1657 } 1658 1659 - **truth-unknown**: This indicates that the Anastasis provider is unaware of 1660 the specified challenge. This is typically a permanent failure, and user 1661 interfaces should not allow users to re-try this challenge. 1662 1663 .. code-block:: json 1664 1665 { 1666 "recovery_state": "CHALLENGE_SELECTING", 1667 "recovery_information": { 1668 "...": "..." 1669 } 1670 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1671 "challenge_feedback": { 1672 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1673 "state": "truth-unknown", 1674 "error_code": 8108 1675 } 1676 } 1677 } 1678 1679 - **rate-limit-exceeded**: This indicates that the user has made too many invalid attempts in too short an amount of time. 1680 1681 .. code-block:: json 1682 1683 { 1684 "recovery_state": "CHALLENGE_SELECTING", 1685 "recovery_information": { 1686 "...": "..." 1687 } 1688 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1689 "challenge_feedback": { 1690 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1691 "state": "rate-limit-exceeded", 1692 "error_code": 8121 1693 } 1694 } 1695 } 1696 1697 - **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. 1698 1699 .. code-block:: json 1700 1701 { 1702 "recovery_state": "CHALLENGE_SELECTING", 1703 "recovery_information": { 1704 "...": "..." 1705 } 1706 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1707 "challenge_feedback": { 1708 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1709 "state": "authentication-timeout", 1710 "error_code": 8122 1711 } 1712 } 1713 } 1714 1715 - **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. 1716 1717 .. code-block:: json 1718 1719 { 1720 "recovery_state": "CHALLENGE_SELECTING", 1721 "recovery_information": { 1722 "...": "..." 1723 } 1724 "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0", 1725 "challenge_feedback": { 1726 "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": { 1727 "state": "external-instructions", 1728 "method": "iban", 1729 "async": true, // optional 1730 "answer_code": 987654321, // optional 1731 "details": { 1732 "...": "..." 1733 } 1734 } 1735 } 1736 } 1737 1738 If "async" is "true", then the client should 1739 poll for the challenge being satisfied using 1740 the "answer_code" that has been provided. 1741 1742 The specific instructions on how to satisfy 1743 the challenge depend on the ``method``. 1744 They include: 1745 1746 - **iban**: The user must perform a wire transfer from their account to the Anastasis provider. 1747 1748 .. code-block:: json 1749 1750 { 1751 "challenge_amount": "EUR:1", 1752 "credit_iban": "DE12345789000", 1753 "business_name": "Data Loss Incorporated", 1754 "wire_transfer_subject": "Anastasis 987654321" 1755 } 1756 1757 Note that the actual wire transfer subject must contain both 1758 the numeric ``answer_code`` as well as 1759 the string ``Anastasis``. 1760 1761 **poll:** 1762 1763 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: 1764 1765 .. code-block:: json 1766 1767 { 1768 "timeout" : { "d_ms" : 5000 }, 1769 } 1770 1771 1772 **pay:** 1773 1774 With a ``pay`` transition, the application indicates to the reducer that 1775 a payment may have been made. Here, it is again possible to specify an 1776 optional ``timeout`` argument for long-polling, for example: 1777 1778 .. code-block:: json 1779 1780 { 1781 "payment_secret": "ABCDADF242525AABASD52525235ABABFDABABANALASDAAKASDAS" 1782 "timeout" : { "d_ms" : 5000 }, 1783 } 1784 1785 Depending on the type of the challenge and the result of the operation, the 1786 new state may be ``CHALLENGE_SOLVING`` (if say the SMS was now sent to the 1787 user), ``CHALLENGE_SELECTING`` (if the answer to the security question was 1788 correct), ``RECOVERY_FINISHED`` (if this was the last challenge that needed to 1789 be solved) or still ``CHALLENGE_PAYING`` (if the challenge was not actually 1790 paid for). For sample messages, see the different types of 1791 ``challenge_feedback`` in the section about ``select_challenge``. 1792 1793 1794 **solve_challenge:** 1795 1796 Solving a challenge takes various formats, depending on the type of the 1797 challenge and what is known about the answer. The different supported 1798 formats are: 1799 1800 .. code-block:: json 1801 1802 { 1803 "answer": "answer to security question" 1804 } 1805 1806 .. code-block:: json 1807 1808 { 1809 "pin": 1234 1810 } 1811 1812 .. code-block:: json 1813 1814 { 1815 "hash": "SOMEBASE32ENCODEDHASHVALUE" 1816 }