From 10634c6ad93504d58061c54a120caf8f50713a39 Mon Sep 17 00:00:00 2001 From: Chris Wagner Date: Mon, 27 May 2024 17:25:01 -0700 Subject: [PATCH] Improve docs w.r.t. USERNAME (#981) --- docs/configuration.rst | 23 ++++--- docs/features.rst | 108 +++++++++++++++----------------- flask_security/username_util.py | 2 +- flask_security/utils.py | 16 ++--- 4 files changed, 77 insertions(+), 72 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index ee7c20fd..ee020978 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -54,7 +54,9 @@ These configuration keys are used globally across all features. Default: ``None``. .. py:data:: SECURITY_FLASH_MESSAGES - Specifies whether or not to flash messages during security procedures. + Specifies whether or not to flash messages for actions certain endpoint perform. + Normally Flash-Security views will flash informational or error messages only when the operation + results in a redirect. Default: ``True``. .. py:data:: SECURITY_I18N_DOMAIN @@ -385,7 +387,7 @@ These configuration keys are used globally across all features. Mapping functions take a single argument - ``identity`` from the form and should return ``None`` if the ``identity`` argument isn't in a format suitable for the attribute. If the ``identity`` argument format matches, it - should be returned, optionally having had some canonicalization performed. + should be returned, optionally having had some normalization performed. The returned result will be used to look up the identity in the UserDataStore using the column name specified in the key. @@ -393,7 +395,7 @@ These configuration keys are used globally across all features. phone number normalization using the ``phonenumbers`` package. .. tip:: - If your mapper performs any sort of canonicalization/normalization, + If your mapper performs any sort of normalization, make sure you apply the exact same transformation in your form validator when setting the field. @@ -822,6 +824,13 @@ Registerable a username field added. This requires that your user model contain the field ``username``. It MUST be set as 'unique' and if you don't want to require a username, it should be set as 'nullable'. + The form validators will call :meth:`.UsernameUtil.validate`. + + In addition, :data:`SECURITY_USER_IDENTITY_ATTRIBUTES` will be updated to include:: + + {"username": {"mapper": uia_username_mapper}, "case_insensitive": True} + + See :meth:`flask_security.uia_username_mapper` for details. If you already have added a username field to your forms, don't set this option - the system will throw an exception at init_app time. @@ -1408,14 +1417,14 @@ Unified Signin Removing it from here won't stop users from using the :data:`SECURITY_LOGIN_URL` endpoint (unless you replace the login endpoint using :py:data:`SECURITY_US_SIGNIN_REPLACES_LOGIN`). - This config variable defines which methods can be used to provide authentication data. - :py:data:`SECURITY_USER_IDENTITY_ATTRIBUTES` controls what sorts of identities can be used. + This config variable defines which methods can be used to provide ``passcode`` data. + :py:data:`SECURITY_USER_IDENTITY_ATTRIBUTES` defines which user model fields can be used as ``identity``. Default: ``["password", "email", "authenticator", "sms"]`` - which are the only supported options. .. py:data:: SECURITY_US_MFA_REQUIRED - A list of ``US_ENABLED_METHODS`` that will require two-factor + A list of :data:`SECURITY_US_ENABLED_METHODS` that will require two-factor authentication. This is of course dependent on the settings of :py:data:`SECURITY_TWO_FACTOR` and :py:data:`SECURITY_TWO_FACTOR_REQUIRED`. Note that even with REQUIRED, only methods listed here will trigger a two-factor cycle. @@ -1654,7 +1663,7 @@ WebAuthn - ``"secondary"`` - just keys registered as "secondary" are allowed If list is empty or ``None`` WebAuthn keys aren't allowed. This also means that the - :py:data:`SECURITY_WAN_VERIFY_URL` endpoint won't be registered. + :py:data:`SECURITY_WAN_VERIFY_URL` endpoint won't be registered. Default: ``["first", "secondary"]`` diff --git a/docs/features.rst b/docs/features.rst index aaf478f6..7e303f5f 100644 --- a/docs/features.rst +++ b/docs/features.rst @@ -99,6 +99,57 @@ expiry value (settable via the :data:`SECURITY_TOKEN_EXPIRE_TIMESTAMP` callable) there are some endpoints that require session information (e.g. a session cookie). Please read :ref:`freshness_topic` and :ref:`csrf_topic` +User Registration +----------------- +If :ref:`configured`, Flask-Security provides a basic user registration view. This view is +very simple and new users need only supply an email address and their password. +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 +able to authenticate with EITHER email or username - however that can be changed via the +:py:data:`SECURITY_USER_IDENTITY_ATTRIBUTES`. + +Email Confirmation +------------------ +If :ref:`configured`, your application +can require that new users confirm their email address prior to allowing them to authenticate. +Flask-Security will send an email message to any new users with a confirmation +link. Upon navigating to the confirmation link, the user's account will be set to +'confirmed'. The user can then sign in usually the normal mechanisms. +There is also view for resending a confirmation link to a given email +if the user happens to try to use an expired token or has lost the previous +email. Confirmation links can be configured to expire after a specified amount +of time (default 5 days). + +Password Reset/Recovery +----------------------- +If :ref:`configured`, +password reset and recovery is available for when a user forgets their +password. Flask-Security sends an email to the user with a link to a view which +allows them to reset their password. Once the password is reset they are redirected to +the login page where they need to authenticate using the new password. +Password reset links can be configured to expire after a specified amount of time. + +As with password change - this will update the the user's ``fs_uniquifier`` attribute +which will invalidate all existing sessions AND (by default) all authentication tokens. + +Password Change +--------------- +If :ref:`configured` users can change their password. Unlike password +recovery, this endpoint is used when the user is already authenticated. The result +of a successful password change is not only a new password, but a new value for ``fs_uniquifier``. +This has the effect is immediately invalidating all existing sessions. The change request +itself effectively re-logs in the user so a new session is created. Note that since the user +is effectively re-logged in, the same signals are sent as when the user normally authenticates. + +*NOTE*: The ``fs_uniquifier`` by default, controls both sessions and authenticated tokens. +Thus changing the password also invalidates all authentication tokens. This may not be desirable +behavior, so if the UserModel contains an attribute ``fs_token_uniquifier``, then that will be used +when generating authentication tokens and so won't be affected by password changes. + Two-factor Authentication ---------------------------------------- If :ref:`configured`, @@ -177,66 +228,12 @@ WebAuthn is a standardized protocol that connects authenticators (such as YubiKe with websites. If :ref:`configured`, Flask-Security supports using WebAuthn keys as either 'first' or 'secondary' authenticators. Please read :ref:`webauthn_topic` for more details. -Email Confirmation ------------------- -If :ref:`configured`, your application -can require that new users confirm their email address prior to allowing them to authenticate. -Flask-Security will send an email message to any new users with a confirmation -link. Upon navigating to the confirmation link, the user's account will be set to -'confirmed'. The user can then sign in usually the normal mechanisms. -There is also view for resending a confirmation link to a given email -if the user happens to try to use an expired token or has lost the previous -email. Confirmation links can be configured to expire after a specified amount -of time (default 5 days). - -Password Reset/Recovery ------------------------ -If :ref:`configured`, -password reset and recovery is available for when a user forgets their -password. Flask-Security sends an email to the user with a link to a view which -allows them to reset their password. Once the password is reset they are redirected to -the login page where they need to authenticate using the new password. -Password reset links can be configured to expire after a specified amount of time. - -As with password change - this will update the the user's ``fs_uniquifier`` attribute -which will invalidate all existing sessions AND (by default) all authentication tokens. - - -User Registration ------------------ -If :ref:`configured`, Flask-Security provides a basic user registration view. This view is -very simple and new users need only supply an email address and their password. -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 -able to authenticate with EITHER email or username - however that can be changed via the -:py:data:`SECURITY_USER_IDENTITY_ATTRIBUTES`. - -Password Change ---------------- -If :ref:`configured` users can change their password. Unlike password -recovery, this endpoint is used when the user is already authenticated. The result -of a successful password change is not only a new password, but a new value for ``fs_uniquifier``. -This has the effect is immediately invalidating all existing sessions. The change request -itself effectively re-logs in the user so a new session is created. Note that since the user -is effectively re-logged in, the same signals are sent as when the user normally authenticates. - -*NOTE*: The ``fs_uniquifier`` by default, controls both sessions and authenticated tokens. -Thus changing the password also invalidates all authentication tokens. This may not be desirable -behavior, so if the UserModel contains an attribute ``fs_token_uniquifier``, then that will be used -when generating authentication tokens and so won't be affected by password changes. - Email Change ------------ If :ref:`configured`, users can change the email they registered with. This will send a new confirmation email to the new email address. - Login Tracking -------------- - Flask-Security can, if :ref:`configured`, keep track of basic login events and statistics. They include: @@ -246,10 +243,8 @@ statistics. They include: * Current login IP address * Total login count - 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 @@ -275,7 +270,6 @@ Note: All registration requests done through JSON/Ajax utilize the ``confirm_reg Command Line Interface ---------------------- - Basic `Click`_ commands for managing users and roles are automatically registered. They can be completely disabled or their names can be changed. Run ``flask --help`` and look for users and roles. diff --git a/flask_security/username_util.py b/flask_security/username_util.py index 473f5529..460161d1 100644 --- a/flask_security/username_util.py +++ b/flask_security/username_util.py @@ -45,7 +45,7 @@ def check_username(self, username: str) -> str | None: Given a username - check for allowable character categories. This is broken out so applications can easily override this method only. - By default allow letters and numbers (using unicodedata.category). + By default, allow letters and numbers (using unicodedata.category). Returns None if allowed, error message if not allowed. """ diff --git a/flask_security/utils.py b/flask_security/utils.py index 8e7a748b..2082ad58 100644 --- a/flask_security/utils.py +++ b/flask_security/utils.py @@ -891,7 +891,7 @@ def get_identity_attributes(app: Flask | None = None) -> list[str]: def get_identity_attribute(attr: str, app: Flask | None = None) -> dict[str, t.Any]: - """Given an user_identity_attribute, return the defining dict. + """Given a user_identity_attribute, return the defining dict. A bit annoying since USER_IDENTITY_ATTRIBUTES is a list of dict where each dict has just one key. """ @@ -910,7 +910,7 @@ def lookup_identity(identity): """ Lookup identity in DB. This loops through, in order, :py:data:`SECURITY_USER_IDENTITY_ATTRIBUTES`, - and first calls the mapper function to validate/normalize. + and first calls the mapper function to normalize. Then the db.find_user is called on the specified user model attribute. """ for mapping in config_value("USER_IDENTITY_ATTRIBUTES"): @@ -926,8 +926,8 @@ def lookup_identity(identity): def uia_phone_mapper(identity: str) -> str | None: - """Used to match identity as a phone number. This is a simple proxy - to :py:class:`PhoneUtil` + """Used to normalize a phone number. This is a simple proxy + to :py:meth:`PhoneUtil.get_canonical_form` See :py:data:`SECURITY_USER_IDENTITY_ATTRIBUTES`. @@ -938,7 +938,9 @@ def uia_phone_mapper(identity: str) -> str | None: def uia_email_mapper(identity: str) -> str | None: - """Used to match identity as an email. + """Used to normalize identity as an email. + This is a simple proxy + to :py:meth:`MailUtil.normalize` :return: Normalized email or None if not valid email. @@ -954,8 +956,8 @@ def uia_email_mapper(identity: str) -> str | None: def uia_username_mapper(identity: str) -> str | None: - """Used to match identity as a username. This is a simple proxy - to :py:class:`UsernameUtil` + """Used to normalize a username. This is a simple proxy + to :py:meth:`UsernameUtil.normalize` See :py:data:`SECURITY_USER_IDENTITY_ATTRIBUTES`.