diff --git a/docs/chapter-13.rst b/docs/chapter-13.rst index a9d8ca55..2322f359 100644 --- a/docs/chapter-13.rst +++ b/docs/chapter-13.rst @@ -226,38 +226,45 @@ Also notice this method can override the code and return a new one. two_factor_validate ^^^^^^^^^^^^^^^^^^^ -By default, py4web will validate the user input in the two factor form by comparing the number entered by the user with the code generated and sent using +By default, py4web will validate the user input in the two factor form by comparing the code entered by the user with the code generated and sent using ``two_factor_send``. However, sometimes it may be useful to define a custom validation of this user-entered code. For instance, if one would like to use the TOTP (or the Time-Based One-Time-Passwords) as the two factor authentication method, the validation requires comparing the code entered by the user with the -value generated at the same time at the server side. Hence, it is not sufficient to generate that value when showing the form (using for instance +value generated at the same time at the server side. Hence, it is not sufficient to generate that value earlier when showing the form (using for instance ``two_factor_send`` method), because by the time the user submits the form, the current valid value may already be different. Instead, this value should be generated when validating the form submitted by the user. -To accomplish such custom validation, the ``two_factor_validate`` method is available. It takes one argument ``form``, which is the two factor authentication -form. The primary use-case for this method is validation of time-based passwords. +To accomplish such custom validation, the ``two_factor_validate`` method is available. It takes two arguments - the current user and the code that was entered +by the user into the two factor authentication form. The primary use-case for this method is validation of time-based passwords. -This example shows how to validate a time-based two factor code with an optional fallback to using the code returned by the ``two_factor_send`` method: +This example shows how to validate a time-based two factor code: .. code:: python - def validate_code(form): - # get the user id - user_id = auth.session.get("auth.2fa_user") - + def validate_code(user, code): try: # get the correct code from an external function correct_code = generate_time_based_code(user_id) except Exception as e: - # otherwise use the code that was returned by two_factor_send method - correct_code = auth.session.get("auth.2fa_code") + # return None to indicate that validation could not be performed + return None # compare the value entered in the auth form with the correct code - if form.vars['authentication_code'] != correct_code: - form.errors['authentication_code'] = "Incorrect code." + if code == correct_code: + return True + else: + return False + +The ``validate_code`` method must return one of three values: + +- ``True`` - if the validation succeded, +- ``False`` - if the validation failed, +- ``None`` - if the validation was not possible for any reason Notice that - if defined - this method is _always_ called to validate the two factor authentication form. It is up to you to decide what kind of validation it -does, but if it is used in conjunction with ``two_factor_send`` method, it also needs to be able to validate the user input against the code that was sent to -the user. +does. If the returned value is ``True``, the user input will be accepted as valid. If the returned value is ``False`` then the user input will be rejected as +invalid, number of tries will be decresed by one, and user will be asked to try again. If the returned value is ``None`` the user input will be checked against +the code generated with the use of ``two_factor_send`` method and the final result will depend on that comparison. In this case authentication will fail if ``two_factor_send`` +method was not defined, and hence no code was sent to the user. .. code:: python