diff --git a/docs/configuration.rst b/docs/configuration.rst index e6874673..4ef2f86b 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -1557,7 +1557,9 @@ Username-Recovery .. py:data:: SECURITY_USERNAME_RECOVERY - Specifies whether username recovery is enabled. + Specifies whether username recovery is enabled. If set to ``True`` the UserModel + must contain a column ``"username"``. Note that this feature is independent + of the :py:data:`SECURITY_USERNAME_ENABLE` feature. Default: ``False``. @@ -1888,7 +1890,7 @@ Social Oauth Feature Flags ------------- -All feature flags. By default all are 'False'/not enabled. +All feature flags. By default all are ``False``/not enabled. * :py:data:`SECURITY_CHANGE_EMAIL` * :py:data:`SECURITY_CONFIRMABLE` @@ -1908,23 +1910,23 @@ URLs and Views -------------- A list of all URLs and Views: -* :py:data:`SECURITY_LOGIN_URL` -* :py:data:`SECURITY_LOGOUT_URL` -* :py:data:`SECURITY_VERIFY_URL` -* :py:data:`SECURITY_REGISTER_URL` -* :py:data:`SECURITY_CHANGE_EMAIL_URL` -* :py:data:`SECURITY_CHANGE_EMAIL_CONFIRM_URL` -* :py:data:`SECURITY_RESET_URL` -* :py:data:`SECURITY_CHANGE_URL` -* :py:data:`SECURITY_CONFIRM_URL` -* :py:data:`SECURITY_MULTI_FACTOR_RECOVERY_CODES_URL` -* :py:data:`SECURITY_MULTI_FACTOR_RECOVERY_URL` -* :py:data:`SECURITY_OAUTH_START_URL` -* :py:data:`SECURITY_OAUTH_RESPONSE_URL` -* :py:data:`SECURITY_TWO_FACTOR_SELECT_URL` -* :py:data:`SECURITY_TWO_FACTOR_SETUP_URL` -* :py:data:`SECURITY_TWO_FACTOR_TOKEN_VALIDATION_URL` -* :py:data:`SECURITY_TWO_FACTOR_RESCUE_URL` +* :py:data:`SECURITY_LOGIN_URL` ``"/login"`` +* :py:data:`SECURITY_LOGOUT_URL` ``"/logout"`` +* :py:data:`SECURITY_VERIFY_URL` ``"/verify"`` +* :py:data:`SECURITY_REGISTER_URL` ``"/register"`` +* :py:data:`SECURITY_CHANGE_EMAIL_URL` ``"change-email"`` +* :py:data:`SECURITY_CHANGE_EMAIL_CONFIRM_URL` ``"/change-email-confirm"`` +* :py:data:`SECURITY_RESET_URL` ``"/reset"`` +* :py:data:`SECURITY_CHANGE_URL` ``"/change"`` +* :py:data:`SECURITY_CONFIRM_URL` ``"/confirm"`` +* :py:data:`SECURITY_MULTI_FACTOR_RECOVERY_CODES_URL` ``"/mf-recovery-codes"`` +* :py:data:`SECURITY_MULTI_FACTOR_RECOVERY_URL` ``"/mf-recovery"`` +* :py:data:`SECURITY_OAUTH_START_URL` ``"/login/oauthstart"`` +* :py:data:`SECURITY_OAUTH_RESPONSE_URL` ``"/login/oauthresponse"`` +* :py:data:`SECURITY_TWO_FACTOR_SELECT_URL` ``"/tf-select"`` +* :py:data:`SECURITY_TWO_FACTOR_SETUP_URL` ``"/tf-setup"`` +* :py:data:`SECURITY_TWO_FACTOR_TOKEN_VALIDATION_URL` ``"/tf-validate"`` +* :py:data:`SECURITY_TWO_FACTOR_RESCUE_URL` ``"/tf-rescue"`` * :py:data:`SECURITY_TWO_FACTOR_ERROR_VIEW` * :py:data:`SECURITY_TWO_FACTOR_POST_SETUP_VIEW` * :py:data:`SECURITY_POST_LOGIN_VIEW` diff --git a/docs/customizing.rst b/docs/customizing.rst index e579bd0b..3e833d19 100644 --- a/docs/customizing.rst +++ b/docs/customizing.rst @@ -478,6 +478,7 @@ welcome_existing SECURITY_SEND_REGISTER_EMAIL SECURITY_EM welcome_existing_username SECURITY_SEND_REGISTER_EMAIL SECURITY_EMAIL_SUBJECT_REGISTER - email user_not_registered SECURITY_RETURN_GENERIC_RESPONSES - username username_recovery SECURITY_USERNAME_RECOVERY SECURITY_EMAIL_SUBJECT_USERNAME_RECOVERY - user username_recovery_email_sent + - username ============================= ================================== ============================================= ====================== =============================== When sending an email, Flask-Security goes through the following steps: diff --git a/docs/features.rst b/docs/features.rst index 99ccc686..c0a82571 100644 --- a/docs/features.rst +++ b/docs/features.rst @@ -120,11 +120,20 @@ This view can be overridden if your registration process requires more fields. User email is validated and normalized using the `email_validator `_ package. -The :py:data:`SECURITY_USERNAME_ENABLE` configuration option, when set to ``True``, will add -support for the user to register a username in addition to an email. By default, the user will be +Username Support +----------------- +Flask-Security supports configuring and using a ``username`` is addition to or instead of an email for +authentication. + +If the :py:data:`SECURITY_USERNAME_ENABLE` configuration option is set to ``True``, ``username`` +will be added to the register and login forms. +By default, the user will be able to authenticate with EITHER email or username - however that can be changed via the :py:data:`SECURITY_USER_IDENTITY_ATTRIBUTES`. +The :py:data:`SECURITY_USERNAME_RECOVERY` option adds an endpoint that allows users +to recover a forgotten username (via email). + Email Confirmation ------------------ If :ref:`configured`, your application @@ -185,8 +194,6 @@ generated and downloaded one-time code (see :py:data:`SECURITY_MULTI_FACTOR_RECO Unified Sign In --------------- -**This feature is in Beta - mostly due to it being brand new and little to no production soak time** - If :ref:`configured`, a generalized login endpoint is provided that takes an `identity` and a `passcode`; where (based on configuration): @@ -260,24 +267,10 @@ JSON/Ajax Support ----------------- Flask-Security supports JSON/Ajax requests where appropriate. Please look at :ref:`csrf_topic` for details on how to work with JSON and -Single Page Applications. More specifically -JSON is supported for the following operations: - -* Login requests -* Unified sign in requests -* Registration requests -* Change password requests -* Change email requests -* Confirmation requests -* Forgot password requests -* Passwordless login requests -* Two-factor login requests -* Change two-factor method requests -* WebAuthn registration and signin requests -* Two-Factor recovery code requests - -In addition, Single-Page-Applications (like those built with Vue, Angular, and -React) are supported via customizable redirect links. +Single Page Applications. + +In addition, :ref:`spa:Working With Single Page Applications` +(like those built with Vue, Angular, and React) are supported via customizable redirect links. Note: All registration requests done through JSON/Ajax utilize the ``confirm_register_form``. @@ -315,7 +308,7 @@ in the `examples` directory. .. _Click: https://palletsprojects.com/p/click/ .. _Flask-Login: https://flask-login.readthedocs.org/en/latest/ -.. _Flask-WTF: https://flask-wtf.readthedocs.io/en/1.0.x/csrf/ +.. _Flask-WTF: https://flask-wtf.readthedocs.io/en/1.2.x/csrf/ .. _alternative token: https://flask-login.readthedocs.io/en/latest/#alternative-tokens .. _Flask-Principal: https://pypi.org/project/Flask-Principal/ .. _documentation on this topic: http://packages.python.org/Flask-Principal/#granular-resource-protection diff --git a/flask_security/core.py b/flask_security/core.py index 2efd9ce1..0a0e99ae 100644 --- a/flask_security/core.py +++ b/flask_security/core.py @@ -1405,6 +1405,7 @@ def __init__( self.two_factor: bool = False self.unified_signin: bool = False self.passwordless: bool = False + self.username_recovery: bool = False self.webauthn: bool = False self.support_mfa: bool = False diff --git a/flask_security/views.py b/flask_security/views.py index c280ee82..d0d1a7a0 100644 --- a/flask_security/views.py +++ b/flask_security/views.py @@ -1289,7 +1289,6 @@ def create_blueprint(app, state, import_name): if state.recoverable: reset_url = cv("RESET_URL", app=app) - username_recovery_url = cv("USERNAME_RECOVERY_URL", app=app) bp.route(reset_url, methods=["GET", "POST"], endpoint="forgot_password")( forgot_password ) @@ -1298,12 +1297,14 @@ def create_blueprint(app, state, import_name): methods=["GET", "POST"], endpoint="reset_password", )(reset_password) - if cv("USERNAME_RECOVERY", app=app): - bp.route( - username_recovery_url, - methods=["GET", "POST"], - endpoint="recover_username", - )(recover_username) + + if state.username_recovery: + username_recovery_url = cv("USERNAME_RECOVERY_URL", app=app) + bp.route( + username_recovery_url, + methods=["GET", "POST"], + endpoint="recover_username", + )(recover_username) if state.changeable: bp.route(