Skip to content

Commit

Permalink
Improve docs w.r.t. USERNAME (#981)
Browse files Browse the repository at this point in the history
  • Loading branch information
jwag956 authored May 28, 2024
1 parent 9616b52 commit 10634c6
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 72 deletions.
23 changes: 16 additions & 7 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -385,15 +387,15 @@ 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.

The provided :meth:`flask_security.uia_phone_mapper` for example performs
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.

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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"]``

Expand Down
108 changes: 51 additions & 57 deletions docs/features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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<configuration:Registerable>`, 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 <https://pypi.org/project/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<configuration:Confirmable>`, 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<configuration:Recoverable>`,
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<configuration:Changeable>` 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<configuration:Two-Factor>`,
Expand Down Expand Up @@ -177,66 +228,12 @@ WebAuthn is a standardized protocol that connects authenticators (such as YubiKe
with websites. If :ref:`configured<configuration:WebAuthn>`, 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<configuration:Confirmable>`, 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<configuration:Recoverable>`,
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<configuration:Registerable>`, 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 <https://pypi.org/project/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<configuration:Changeable>` 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<configuration:Change-Email>`, 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<configuration:Trackable>`, keep track of basic login events and
statistics. They include:

Expand All @@ -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
Expand All @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion flask_security/username_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""
Expand Down
16 changes: 9 additions & 7 deletions flask_security/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""
Expand All @@ -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"):
Expand All @@ -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`.
Expand All @@ -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.
Expand All @@ -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`.
Expand Down

0 comments on commit 10634c6

Please sign in to comment.