FLASK-SECURITY(1)

NAME
flask-security - Flask-Security 3.3.3 Flask-Security: add a drop of security to your Flask application.

Flask-Security allows you to quickly add common security mechanisms to your Flask application. They include:

1.  Session based authentication

2.  Role and Permission management

3.  Password hashing

4.  Basic HTTP authentication

5.  Token based authentication

6.  Token based account activation (optional)

7.  Token based password recovery / resetting (optional)

8.  Two-factor authentication (optional/experimental)

9.  User registration (optional)

10. Login tracking (optional)

11. JSON/Ajax Support

    Many of these features are made possible by integrating various Flask extensions and libraries. They include:

1.  Flask-Login

2.  Flask-Mail

3.  Flask-Principal

4.  Flask-WTF

5.  itsdangerous

6.  passlib

7.  PyQRCode

    Additionally, it assumes you'll be using a common library for your database connections and model definitions. Flask-Security supports the following Flask extensions out of the box for data persistence:

1.  Flask-SQLAlchemy

2.  Flask-MongoEngine

3.  Peewee Flask utils

4.  PonyORM

GETTING STARTED

  Features

Flask-Security allows you to quickly add common security mechanisms to your Flask application. They include:

  Session Based Authentication

Session based authentication is fulfilled entirely by the Flask-Login extension. Flask-Security handles the configuration of Flask-Login automatically based on a few of its own configuration values and uses Flask-Login's alternative token feature for remembering users when their session has expired. Flask-WTF integrates with the session as well to provide out of the box CSRF support. Flask-Security extends that to support requiring CSRF for requests that are authenticated via session cookies, but not for requests authenticated using tokens.

  Role/Identity Based Access

Flask-Security implements very basic role management out of the box. This means that you can associate a high level role or multiple roles to any user. For instance, you may assign roles such as Admin, Editor, SuperUser, or a combination of said roles to a user. Access control is based on the role name and/or permissions contained within the role; and all roles should be uniquely named. This feature is implemented using the Flask-Principal extension. As with basic RBAC, permissions can be assigned to roles to provide more granular access control. Permissions can be associated with one or more roles (the RoleModel contains a list of permissions). The values of permissions are completely up to the developer - Flask-Security simply treats them as strings. If you'd like to implement even more granular access control (such as per-object), you can refer to the Flask-Principal documentation on this topic.

  Password Hashing

Password hashing is enabled with passlib. Passwords are hashed with the bcrypt function by default but you can easily configure the hashing algorithm. You should always use a hashing algorithm in your production environment. Hash algorithms not listed in SECURITY_PASSWORD_SINGLE_HASH will be double hashed - first an HMAC will be computed, then the selected hash function will be used. In this case - you must provide a SECURITY_PASSWORD_SALT. A good way to generate this is:

secrets.SystemRandom().getrandbits(128)

Bear in mind passlib does not assume which algorithm you will choose and may require additional libraries to be installed.

  Basic HTTP Authentication

Basic HTTP authentication is achievable using a simple view method decorator. This feature expects the incoming authentication information to identify a user in the system. This means that the username must be equal to their email address.

  Token Authentication

Token based authentication is enabled by retrieving the user auth token by performing an HTTP POST with a query param of include_auth_token with the authentication details as JSON data against the authentication endpoint. A successful call to this endpoint will return the user's ID and their authentication token. This token can be used in subsequent requests to protected resources. The auth token is supplied in the request through an HTTP header or query string parameter. By default the HTTP header name is Authentication-Token and the default query string parameter name is auth_token. Authentication tokens are generated using a uniquifier field in the user's UserModel. If that field is changed (via UserDatastore.set_uniquifier()) then any existing authentication tokens will no longer be valid. Changing the user's password will not affect tokens.

Note that prior to release 3.3.0 or if the UserModel doesn't contain the fs_uniquifier attribute the authentication tokens are generated using the user's password. Thus if the user changes his or her password their existing authentication token will become invalid. A new token will need to be retrieved using the user's new password. Verifying tokens created in this way is very slow.

  Two-factor Authentication (experimental)

Two-factor authentication is enabled by generating time-based one time passwords (Tokens). The tokens are generated using the users totp secret, which is unique per user, and is generated both on first login, and when changing the two-factor method (Doing this causes the previous totp secret to become invalid). The token is provided by one of 3 methods - email, sms (service is not provided), or Google Authenticator. By default, tokens provided by google authenticator are valid for 2 minutes, tokens sent by mail for up to 5 minute and tokens sent by sms for up to 2 minutes. The QR code used to supply Google Authenticator with the secret is generated using the PyQRCode library. This feature is marked experimental meaning that backwards incompatible changes might occur during minor releases. While the feature is operational, it has these known limitations:

•   Limited and incomplete JSON support

•   Incomplete i18n support

•   Not enough documentation to use w/o looking at code

  Email Confirmation

If desired you can require that new users confirm their email address. Flask-Security will send an email message to any new users with a confirmation link. Upon navigating to the confirmation link, the user will be automatically logged in. 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.

  Password Reset/Recovery

Password reset and recovery is available for when a user forgets his or her password. Flask-Security sends an email to the user with a link to a view which they can reset their password. Once the password is reset they are automatically logged in and can use the new password from then on. Password reset links can be configured to expire after a specified amount of time.

  User Registration

Flask-Security comes packaged with 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.

  Login Tracking

Flask-Security can, if configured, keep track of basic login events and statistics. They include:

•   Last login date

•   Current login date

•   Last login IP address

•   Current login IP address

•   Total login count

  JSON/Ajax Support

Flask-Security supports JSON/Ajax requests where appropriate. Please look at csrftopic for details on how to work with JSON and Single Page Applications. More specifically JSON is supported for the following operations:

•   Login requests

•   Registration requests

•   Change password requests

•   Confirmation requests

•   Forgot password requests

•   Passwordless login requests

•   Two-factor login requests

•   Change two-factor method requests

    In addition, Single-Page-Applications (like those built with Vue, Angular, and React) are supported via customizable redirect links.

  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.

  Configuration

The following configuration values are used by Flask-Security:

  Core
SECRET_KEY | This is actually part of Flask - but is used by Flask-Security to sign all tokens. It is critical this is set to a strong value. For python3 consider using: secrets.token_urlsafe()
SECURITY_BLUEPRINT_NAME | Specifies the name for the Flask-Security blueprint. Defaults to security.
SECURITY_CLI_USERS_NAME | Specifies the name for the command managing users. Disable by setting False. Defaults to users.
SECURITY_CLI_ROLES_NAME | Specifies the name for the command managing roles. Disable by setting False. Defaults to roles.
SECURITY_URL_PREFIX | Specifies the URL prefix for the Flask-Security blueprint. Defaults to None.
SECURITY_SUBDOMAIN | Specifies the subdomain for the Flask-Security blueprint. Defaults to None.
SECURITY_FLASH_MESSAGES | Specifies whether or not to flash messages during security procedures. Defaults to True.
SECURITY_I18N_DOMAIN | Specifies the name for domain used for translations. Defaults to flask_security.
SECURITY_I18N_DIRNAME | Specifies the directory containing the MO files used for translations. Defaults to [PATH_LIB]/flask_security/translations.
SECURITY_PASSWORD_HASH | Specifies the password hash algorithm to use when hashing passwords. Recommended values for production systems are bcrypt, argon2, sha512_crypt, or pbkdf2_sha512. Some algorithms require the installation of a backend package (e.g. bcrypt, argon2). Defaults to bcrypt.
SECURITY_PASSWORD_SCHEMES | List of support password hash algorithms. SECURITY_PASSWORD_HASH must be from this list. Passwords encrypted with any of these schemes will be honored.
SECURITY_DEPRECATED_PASSWORD_SCHEMES | List of password hash algorithms that are considered weak and will be accepted, however on first use, will be re-hashed to the current default SECURITY_PASSWORD_HASH. Default is ["auto"] which means any password found that wasn't hashed using SECURITY_PASSWORD_HASH will be re-hashed.
SECURITY_PASSWORD_SALT | Specifies the HMAC salt. This is required for all schemes that are configured for double hashing. A good salt can be generated using: secrets.SystemRandom().getrandbits(128). Defaults to None.
SECURITY_PASSWORD_SINGLE_HASH | A list of schemes that should not be hashed twice. By default, passwords are hashed twice, first with SECURITY_PASSWORD_SALT, and then with a random salt. Defaults to a list of known schemes not working with double hashing (django_{digest}, plaintext).
SECURITY_HASHING_SCHEMES | List of algorithms used for encrypting/hashing sensitive data within a token (Such as is sent with confirmation or reset password). Defaults to sha256_crypt.
SECURITY_DEPRECATED_HASHING_SCHEMES | List of deprecated algorithms used for creating and validating tokens. Defaults to hex_md5.
SECURITY_PASSWORD_HASH_OPTIONS | Specifies additional options to be passed to the hashing method. This is deprecated as of passlib 1.7.
SECURITY_PASSWORD_HASH_PASSLIB_OPTIONS | Pass additional options to the various hashing methods. This is a dict of the form {<scheme>__<option>: <value>, ..} e.g. {"argon2__rounds": 10}.
SECURITY_EMAIL_SENDER | Specifies the email address to send emails as. Defaults to value set to MAIL_DEFAULT_SENDER if Flask-Mail is used otherwise no-reply@localhost.
SECURITY_TWO_FACTOR_RESCUE_MAIL | Specifies the email address users send mail to when they can't complete the two-factor authentication login. Defaults to no-reply@localhost.
SECURITY_TWO_FACTOR_SECRET | Secret used to encrypt totp_password both into DB and in session cookie. Best practice is to set this to '{1: passlib.totp.generate_secret()}'. See: Totp for details.
SECURITY_TOKEN_AUTHENTICATION_KEY | Specifies the query string parameter to read when using token authentication. Defaults to auth_token.
SECURITY_TOKEN_AUTHENTICATION_HEADER | Specifies the HTTP header to read when using token authentication. Defaults to Authentication-Token.
SECURITY_TOKEN_MAX_AGE | Specifies the number of seconds before an authentication token expires. Defaults to None, meaning the token never expires.
SECURITY_DEFAULT_HTTP_AUTH_REALM | Specifies the default authentication realm when using basic HTTP auth. Defaults to Login Required
SECURITY_USE_VERIFY_PASSWORD_CACHE | If True Enables cache for token verification, which speeds up further calls to authenticated routes using authentication-token and slow hash algorithms (like bcrypt). Defaults to None. If you set this - you must ensure that cachetools is installed. Note: this will likely be deprecated and removed in 4.0. It has known limitations, and there is now a better/faster way to generate and verify auth tokens.
SECURITY_VERIFY_HASH_CACHE_MAX_SIZE | Limitation for token validation cache size Rules are the ones of TTLCache of cachetools package. Defaults to 500
SECURITY_VERIFY_HASH_CACHE_TTL | Time to live for password check cache entries. Defaults to 300 (5 minutes)
SECURITY_REDIRECT_BEHAVIOR | Passwordless login, confirmation, and reset password have GET endpoints that validate the passed token and redirect to an action form. For Single-Page-Applications style UIs which need to control their own internal URL routing these redirects need to not contain forms, but contain relevant information as query parameters. Setting this to spa will enable that behavior. Defaults to None which is existing html-style form redirects.
SECURITY_REDIRECT_HOST | Mostly for development purposes, the UI is often developed separately and is running on a different port than the Flask application. In order to test redirects, the netloc of the redirect URL needs to be rewritten. Setting this to e.g. localhost:8080 does that. Defaults to None
SECURITY_CSRF_PROTECT_MECHANISMS | Authentication mechanisms that require CSRF protection. These are the same mechanisms as are permitted in the @auth_required decorator. Defaults to ("basic", "session", "token")
SECURITY_CSRF_IGNORE_UNAUTH_ENDPOINTS | If True then CSRF will not be required for endpoints that don't require authentication (e.g. login, logout, register, forgot_password). Defaults to False
SECURITY_CSRF_COOKIE | A dict that defines the parameters required to set a CSRF cookie. At a minimum it requires a 'key'. The complete set of parameters is described in Flask's set_cookie documentation. Defaults to {"key": None} whic means no cookie will sent.
SECURITY_CSRF_HEADER | The HTTP Header name that will contain the CSRF token. X-XSRF-Token is used by packages such as axios. Defaults to X-XSRF-Token.
SECURITY_CSRF_COOKIE_REFRESH_EACH_REQUEST | By default, csrf_tokens have an expiration (controlled the the configuration variable WTF_CSRF_TIME_LIMIT. This can cause CSRF failures if say an application is left idle for a long time. You can set that time limit to None or have the CSRF cookie sent on every request (which will give it a new expiration time). Defaults to False.

  URLs and Views
SECURITY_LOGIN_URL | Specifies the login URL. Defaults to /login.
SECURITY_LOGOUT_URL | Specifies the logout URL. Defaults to /logout.
SECURITY_REGISTER_URL | Specifies the register URL. Defaults to /register.
SECURITY_RESET_URL | Specifies the password reset URL. Defaults to /reset.
SECURITY_CHANGE_URL | Specifies the password change URL. Defaults to /change.
SECURITY_CONFIRM_URL | Specifies the email confirmation URL. Defaults to /confirm.
SECURITY_TWO_FACTOR_SETUP_URL | Specifies the two factor setup URL. Defaults to /tf-setup.
SECURITY_TWO_FACTOR_TOKEN_VALIDATION_URL | Specifies the two factor token validation URL. Defaults to /tf-validate.
SECURITY_TWO_FACTOR_QRCODE_URL | Specifies the two factor request QrCode URL. Defaults to /tf-qrcode.
SECURITY_TWO_FACTOR_RESCUE_URL | Specifies the two factor rescue URL. Defaults to /tf-rescue.
SECURITY_TWO_FACTOR_CONFIRM_URL | Specifies the two factor password confirmation URL. Defaults to /tf-confirm.
SECURITY_POST_LOGIN_VIEW | Specifies the default view to redirect to after a user logs in. This value can be set to a URL or an endpoint name. Defaults to /.
SECURITY_POST_LOGOUT_VIEW | Specifies the default view to redirect to after a user logs out. This value can be set to a URL or an endpoint name. Defaults to /.
SECURITY_CONFIRM_ERROR_VIEW | Specifies the view to redirect to if a confirmation error occurs. This value can be set to a URL or an endpoint name. If this value is None, the user is presented the default view to resend a confirmation link. In the case of SECURITY_REDIRECT_BEHAVIOR == spa query params in the redirect will contain the error. Defaults to``None``.
SECURITY_POST_REGISTER_VIEW | Specifies the view to redirect to after a user successfully registers. This value can be set to a URL or an endpoint name. If this value is None, the user is redirected to the value of SECURITY_POST_LOGIN_VIEW. Defaults to None.
SECURITY_POST_CONFIRM_VIEW | Specifies the view to redirect to after a user successfully confirms their email. This value can be set to a URL or an endpoint name. If this value is None, the user is redirected to the value of SECURITY_POST_LOGIN_VIEW. Defaults to None.
SECURITY_POST_RESET_VIEW | Specifies the view to redirect to after a user successfully resets their password. This value can be set to a URL or an endpoint name. If this value is None, the user is redirected to the value of SECURITY_POST_LOGIN_VIEW. Defaults to None.
SECURITY_POST_CHANGE_VIEW | Specifies the view to redirect to after a user successfully changes their password. This value can be set to a URL or an endpoint name. If this value is None, the user is redirected to the value of SECURITY_POST_LOGIN_VIEW. Defaults to None.
SECURITY_UNAUTHORIZED_VIEW | Specifies the view to redirect to if a user attempts to access a URL/endpoint that they do not have permission to access. If this value is None, the user is presented with a default HTTP 403 response. Defaults to None.
SECURITY_RESET_VIEW | Specifies the view/URL to redirect to after a GET reset-password link. This is only valid if SECURITY_REDIRECT_BEHAVIOR == spa. Query params in the redirect will contain the token and email. Defaults to None
SECURITY_RESET_ERROR_VIEW | Specifies the view/URL to redirect to after a GET reset-password link when there is an error. This is only valid if SECURITY_REDIRECT_BEHAVIOR == spa. Query params in the redirect will contain the error. Defaults to None
SECURITY_LOGIN_ERROR_VIEW | Specifies the view/URL to redirect to after a GET passwordless link when there is an error. This is only valid if SECURITY_REDIRECT_BEHAVIOR == spa. Query params in the redirect will contain the error. Defaults to None

  Template Paths
SECURITY_FORGOT_PASSWORD_TEMPLATE | Specifies the path to the template for the forgot password page. Defaults to security/forgot_password.html.
SECURITY_LOGIN_USER_TEMPLATE | Specifies the path to the template for the user login page. Defaults to security/login_user.html.
SECURITY_REGISTER_USER_TEMPLATE | Specifies the path to the template for the user registration page. Defaults to security/register_user.html.
SECURITY_RESET_PASSWORD_TEMPLATE | Specifies the path to the template for the reset password page. Defaults to security/reset_password.html.
SECURITY_CHANGE_PASSWORD_TEMPLATE | Specifies the path to the template for the change password page. Defaults to security/change_password.html.
SECURITY_SEND_CONFIRMATION_TEMPLATE | Specifies the path to the template for the resend confirmation instructions page. Defaults to security/send_confirmation.html.
SECURITY_SEND_LOGIN_TEMPLATE | Specifies the path to the template for the send login instructions page for passwordless logins. Defaults to security/send_login.html.
SECURITY_TWO_FACTOR_VERIFY_CODE_TEMPLATE | Specifies the path to the template for the verify code page for the two-factor authentication process. Defaults to security/two_factor_verify_code.html.
SECURITY_TWO_FACTOR_SETUP_TEMPLATE | Specifies the path to the template for the setup page for the two factor authentication process. Defaults to security/two_factor_setup.html
SECURITY_TWO_FACTOR_VERIFY_PASSWORD_TEMPLATE | Specifies the path to the template for the change method page for the two factor authentication process. Defaults to security/two_factor_verify_password.html.

  Feature Flags
SECURITY_CONFIRMABLE | Specifies if users are required to confirm their email address when registering a new account. If this value is True, Flask-Security creates an endpoint to handle confirmations and requests to resend confirmation instructions. The URL for this endpoint is specified by the SECURITY_CONFIRM_URL configuration option. Defaults to False.
SECURITY_REGISTERABLE | Specifies if Flask-Security should create a user registration endpoint. The URL for this endpoint is specified by the SECURITY_REGISTER_URL configuration option. Defaults to False.
SECURITY_RECOVERABLE | Specifies if Flask-Security should create a password reset/recover endpoint. The URL for this endpoint is specified by the SECURITY_RESET_URL configuration option. Defaults to False.
SECURITY_TRACKABLE | Specifies if Flask-Security should track basic user login statistics. If set to True, ensure your models have the required fields/attributes and make sure to commit changes after calling login_user. Be sure to use ProxyFix if you are using a proxy. Defaults to False
SECURITY_PASSWORDLESS | Specifies if Flask-Security should enable the passwordless login feature. If set to True, users are not required to enter a password to login but are sent an email with a login link. This feature is experimental and should be used with caution. Defaults to False.
SECURITY_CHANGEABLE | Specifies if Flask-Security should enable the change password endpoint. The URL for this endpoint is specified by the SECURITY_CHANGE_URL configuration option. Defaults to False.
SECURITY_TWO_FACTOR | Specifies if Flask-Security should enable the two-factor login feature. If set to True, in addition to their passwords, users will be required to enter a code that is sent to them. Note that unless SECURITY_TWO_FACTOR_REQUIRED is set - this is opt-in. Defaults to False.

  Email
SECURITY_EMAIL_SUBJECT_REGISTER | Sets the subject for the confirmation email. Defaults to Welcome
SECURITY_EMAIL_SUBJECT_PASSWORDLESS | Sets the subject for the passwordless feature. Defaults to Login instructions
SECURITY_EMAIL_SUBJECT_PASSWORD_NOTICE | Sets subject for the password notice. Defaults to Your password has been reset
SECURITY_EMAIL_SUBJECT_PASSWORD_RESET | Sets the subject for the password reset email. Defaults to Password reset instructions
SECURITY_EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE | Sets the subject for the password change notice. Defaults to Your password has been changed
SECURITY_EMAIL_SUBJECT_CONFIRM | Sets the subject for the email confirmation message. Defaults to Please confirm your email
SECURITY_EMAIL_PLAINTEXT | Sends email as plaintext using *.txt template. Defaults to True.
SECURITY_EMAIL_HTML | Sends email as HTML using *.html template. Defaults to True.
SECURITY_EMAIL_SUBJECT_TWO_FACTOR | Sets the subject for the two factor feature. Defaults to Two-factor Login
SECURITY_EMAIL_SUBJECT_TWO_FACTOR_RESCUE | Sets the subject for the two factor help function. Defaults to Two-factor Rescue

  Miscellaneous
SECURITY_USER_IDENTITY_ATTRIBUTES | Specifies which attributes of the user object can be used for login. Defaults to ['email'].
SECURITY_SEND_REGISTER_EMAIL | Specifies whether registration email is sent. Defaults to True.
SECURITY_SEND_PASSWORD_CHANGE_EMAIL | Specifies whether password change email is sent. Defaults to True.
SECURITY_SEND_PASSWORD_RESET_EMAIL | Specifies whether password reset email is sent. Defaults to True.
SECURITY_SEND_PASSWORD_RESET_NOTICE_EMAIL | Specifies whether password reset notice email is sent. Defaults to True.
SECURITY_CONFIRM_EMAIL_WITHIN | Specifies the amount of time a user has before their confirmation link expires. Always pluralized the time unit for this value. Defaults to 5 days.
SECURITY_RESET_PASSWORD_WITHIN | Specifies the amount of time a user has before their password reset link expires. Always pluralized the time unit for this value. Defaults to 5 days.
SECURITY_LOGIN_WITHIN | Specifies the amount of time a user has before a login link expires. This is only used when the passwordless login feature is enabled. Always pluralize the time unit for this value. Defaults to 1 days.
SECURITY_AUTO_LOGIN_AFTER_CONFIRM | If False then on confirmation the user will be required to login again. Note that the confirmation token is not valid after being used once. If True, then the user corresponding to the confirmation token will be automatically logged in. Defaults to True.
SECURITY_TWO_FACTOR_GOOGLE_AUTH_VALIDITY | Specifies the number of seconds access token is valid. Defaults to 2 minutes.
SECURITY_TWO_FACTOR_MAIL_VALIDITY | Specifies the number of seconds access token is valid. Defaults to 5 minutes.
SECURITY_TWO_FACTOR_SMS_VALIDITY | Specifies the number of seconds access token is valid. Defaults to 2 minutes.
SECURITY_LOGIN_WITHOUT_CONFIRMATION | Specifies if a user may login before confirming their email when the value of SECURITY_CONFIRMABLE is set to True. Defaults to False.
SECURITY_CONFIRM_SALT | Specifies the salt value when generating confirmation links/tokens. Defaults to confirm-salt.
SECURITY_RESET_SALT | Specifies the salt value when generating password reset links/tokens. Defaults to reset-salt.
SECURITY_LOGIN_SALT | Specifies the salt value when generating login links/tokens. Defaults to login-salt.
SECURITY_REMEMBER_SALT | Specifies the salt value when generating remember tokens. Remember tokens are used instead of user ID's as it is more secure. Defaults to remember-salt.
SECURITY_DEFAULT_REMEMBER_ME | Specifies the default "remember me" value used when logging in a user. Defaults to False.
SECURITY_TWO_FACTOR_REQUIRED | If set to True then all users will be required to setup and use two factor authorization. Defaults to False.
SECURITY_TWO_FACTOR_ENABLED_METHODS | Specifies the default enabled methods for two-factor authentication. Defaults to ['mail', 'google_authenticator', 'sms'] which are the only supported method at the moment.
SECURITY_TWO_FACTOR_URI_SERVICE_NAME | Specifies the name of the service or application that the user is authenticating to. Defaults to service_name
SECURITY_TWO_FACTOR_SMS_SERVICE | Specifies the name of the sms service provider. Defaults to Dummy which does nothing.
SECURITY_TWO_FACTOR_SMS_SERVICE_CONFIG | Specifies a dictionary of basic configurations needed for use of a sms service. Defaults to {'ACCOUNT_ID': NONE, 'AUTH_TOKEN ':NONE, 'PHONE_NUMBER': NONE}
SECURITY_DATETIME_FACTORY | Specifies the default datetime factory. Defaults to datetime.datetime.utcnow.
SECURITY_BACKWARDS_COMPAT_UNAUTHN | If set to True then the default behavior for authentication failures from one of Flask-Security's decorators will be restored to be compatible with releases prior to 3.3.0 (return 401 and some static html). Defaults to False.
SECURITY_BACKWARDS_COMPAT_AUTH_TOKEN | If set to True then an Authentication-Token will be returned on every successful call to login, reset-password, change-password as part of the JSON response. This was the default prior to release 3.3.0 - however sending Authentication-Tokens (which by default don't expire) to session based UIs is a bad security practice. Defaults to False.
SECURITY_BACKWARDS_COMPAT_AUTH_TOKEN_INVALIDATE | When True changing the user's password will also change the user's fs_uniquifier (if it exists) such that existing authentication tokens will be rendered invalid. This restores pre 3.3.0 behavior.

  Messages

The following are the messages Flask-Security uses. They are tuples; the first element is the message and the second element is the error level.

The default messages and error levels can be found in core.py.

•   SECURITY_MSG_ALREADY_CONFIRMED

•   SECURITY_MSG_ANONYMOUS_USER_REQUIRED

•   SECURITY_MSG_CONFIRMATION_EXPIRED

•   SECURITY_MSG_CONFIRMATION_REQUEST

•   SECURITY_MSG_CONFIRMATION_REQUIRED

•   SECURITY_MSG_CONFIRM_REGISTRATION

•   SECURITY_MSG_DISABLED_ACCOUNT

•   SECURITY_MSG_EMAIL_ALREADY_ASSOCIATED

•   SECURITY_MSG_EMAIL_CONFIRMED

•   SECURITY_MSG_EMAIL_NOT_PROVIDED

•   SECURITY_MSG_FORGOT_PASSWORD

•   SECURITY_MSG_INVALID_CONFIRMATION_TOKEN

•   SECURITY_MSG_INVALID_EMAIL_ADDRESS

•   SECURITY_MSG_INVALID_LOGIN_TOKEN

•   SECURITY_MSG_INVALID_PASSWORD

•   SECURITY_MSG_INVALID_REDIRECT

•   SECURITY_MSG_INVALID_RESET_PASSWORD_TOKEN

•   SECURITY_MSG_LOGIN

•   SECURITY_MSG_LOGIN_EMAIL_SENT

•   SECURITY_MSG_LOGIN_EXPIRED

•   SECURITY_MSG_PASSWORDLESS_LOGIN_SUCCESSFUL

•   SECURITY_MSG_PASSWORD_CHANGE

•   SECURITY_MSG_PASSWORD_INVALID_LENGTH

•   SECURITY_MSG_PASSWORD_IS_THE_SAME

•   SECURITY_MSG_PASSWORD_MISMATCH

•   SECURITY_MSG_PASSWORD_NOT_PROVIDED

•   SECURITY_MSG_PASSWORD_NOT_SET

•   SECURITY_MSG_PASSWORD_RESET

•   SECURITY_MSG_PASSWORD_RESET_EXPIRED

•   SECURITY_MSG_PASSWORD_RESET_REQUEST

•   SECURITY_MSG_REFRESH

•   SECURITY_MSG_RETYPE_PASSWORD_MISMATCH

•   SECURITY_MSG_TWO_FACTOR_INVALID_TOKEN

•   SECURITY_MSG_TWO_FACTOR_LOGIN_SUCCESSFUL

•   SECURITY_MSG_TWO_FACTOR_CHANGE_METHOD_SUCCESSFUL

•   SECURITY_MSG_TWO_FACTOR_PASSWORD_CONFIRMATION_DONE

•   SECURITY_MSG_TWO_FACTOR_PASSWORD_CONFIRMATION_NEEDED

•   SECURITY_MSG_TWO_FACTOR_PERMISSION_DENIED

•   SECURITY_MSG_TWO_FACTOR_METHOD_NOT_AVAILABLE

•   SECURITY_MSG_TWO_FACTOR_DISABLED

•   SECURITY_MSG_UNAUTHORIZED

•   SECURITY_MSG_UNAUTHENTICATED

•   SECURITY_MSG_USER_DOES_NOT_EXIST

  Quick Start

There are some complete (but simple) examples available in the examples directory of the Flask-Security repo.

DANGER: The examples below place secrets in source files. Never do this for your application especially if your source code is placed in a public repo. How you pass in secrets securely will depend on your deployment model - however in most cases (e.g. docker, lambda) using environment variables will be the easiest.

•   Basic SQLAlchemy Application

•   Basic SQLAlchemy Application with session

•   Basic MongoEngine Application

•   Basic Peewee Application

•   Mail Configuration

•   Proxy Configuration

•   Unit Testing Your Application

  Basic SQLAlchemy Application

  SQLAlchemy Install requirements

$ mkvirtualenv <your-app-name>
$ pip install flask-security-too flask-sqlalchemy

  SQLAlchemy Application

The following code sample illustrates how to get started as quickly as possible using Flask-SQLAlchemy and the built-in model mixins:

import os
from flask import Flask, render_template_string
from flask_sqlalchemy import SQLAlchemy
from flask_security import Security, SQLAlchemyUserDatastore, auth_required, hash_password
from flask_security.models import fsqla
# Create app
app = Flask(__name__)
app.config['DEBUG'] = True
# Generate a nice key using secrets.token_urlsafe()
app.config['SECRET_KEY'] = os.environ.get("SECRET_KEY", 'pf9Wkove4IKEAXvy-cQkeDPhv9Cb3Ag-wyJILbq_dFw')
# Bcrypt is set as default SECURITY_PASSWORD_HASH, which requires a salt
# Generate a good salt using: secrets.SystemRandom().getrandbits(128)
app.config['SECURITY_PASSWORD_SALT'] = os.environ.get("SECURITY_PASSWORD_SALT", '146585145368132386173505678016728509634')
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
# As of Flask-SQLAlchemy 2.4.0 it is easy to pass in options directly to the
# underlying engine. This option makes sure that DB connections from the
# pool are still valid. Important for entire application since
# many DBaaS options automatically close idle connections.
app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {
    "pool_pre_ping": True,
}
# Create database connection object
db = SQLAlchemy(app)
# Define models
fsqla.FsModels.set_db_info(db)
class Role(db.Model, fsqla.FsRoleMixin):
    pass
class User(db.Model, fsqla.FsUserMixin):
    pass
# Setup Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
# Create a user to test with
@app.before_first_request
def create_user():
    db.create_all()
    user_datastore.create_user(email="test@me.com", password=hash_password("password"))
    db.session.commit()
# Views
@app.route("/")
@auth_required()
def home():
    return render_template_string("Hello {{ current_user.email }}")
if __name__ == '__main__':
    app.run()

  Basic SQLAlchemy Application with session

  SQLAlchemy Install requirements

$ mkvirtualenv <your-app-name>
$ pip install flask-security-too sqlalchemy

Also, you can use the extension Flask-SQLAlchemy-Session documentation.

  SQLAlchemy Application

The following code sample illustrates how to get started as quickly as possible using SQLAlchemy in a declarative way:

We are gonna split the application at least in three files: app.py, database.py and models.py. You can also do the models a folder and spread your tables there.

•   app.py

    import os
    from flask import Flask, render_template_string
    from flask_security import Security, current_user, auth_required, hash_password, \
         SQLAlchemySessionUserDatastore
    from database import db_session, init_db
    from models import User, Role
    # Create app
    app = Flask(__name__)
    app.config['DEBUG'] = True
    # Generate a nice key using secrets.token_urlsafe()
    app.config['SECRET_KEY'] = os.environ.get("SECRET_KEY", 'pf9Wkove4IKEAXvy-cQkeDPhv9Cb3Ag-wyJILbq_dFw')
    # Bcrypt is set as default SECURITY_PASSWORD_HASH, which requires a salt
    # Generate a good salt using: secrets.SystemRandom().getrandbits(128)
    app.config['SECURITY_PASSWORD_SALT'] = os.environ.get("SECURITY_PASSWORD_SALT", '146585145368132386173505678016728509634')
    # Setup Flask-Security
    user_datastore = SQLAlchemySessionUserDatastore(db_session, User, Role)
    security = Security(app, user_datastore)
    # Create a user to test with
    @app.before_first_request
    def create_user():
        init_db()
        user_datastore.create_user(email="test@me.com", password=hash_password("password"))
        db_session.commit()
    # Views
    @app.route("/")
    @auth_required()
    def home():
        return render_template_string('Hello {{email}} !', email=current_user.email)
    if __name__ == '__main__':
        app.run()

•   database.py

    from sqlalchemy import create_engine
    from sqlalchemy.orm import scoped_session, sessionmaker
    from sqlalchemy.ext.declarative import declarative_base
    engine = create_engine('sqlite:////tmp/test.db', \
                           convert_unicode=True)
    db_session = scoped_session(sessionmaker(autocommit=False,
                                             autoflush=False,
                                             bind=engine))
    Base = declarative_base()
    Base.query = db_session.query_property()
    def init_db():
        # import all modules here that might define models so that
        # they will be registered properly on the metadata.  Otherwise
        # you will have to import them first before calling init_db()
        import models
        Base.metadata.create_all(bind=engine)

•   models.py

    from database import Base
    from flask_security import UserMixin, RoleMixin
    from sqlalchemy import create_engine
    from sqlalchemy.orm import relationship, backref
    from sqlalchemy import Boolean, DateTime, Column, Integer, \
                           String, ForeignKey
    class RolesUsers(Base):
        __tablename__ = 'roles_users'
        id = Column(Integer(), primary_key=True)
        user_id = Column('user_id', Integer(), ForeignKey('user.id'))
        role_id = Column('role_id', Integer(), ForeignKey('role.id'))
    class Role(Base, RoleMixin):
        __tablename__ = 'role'
        id = Column(Integer(), primary_key=True)
        name = Column(String(80), unique=True)
        description = Column(String(255))
    class User(Base, UserMixin):
        __tablename__ = 'user'
        id = Column(Integer, primary_key=True)
        email = Column(String(255), unique=True)
        username = Column(String(255))
        password = Column(String(255))
        last_login_at = Column(DateTime())
        current_login_at = Column(DateTime())
        last_login_ip = Column(String(100))
        current_login_ip = Column(String(100))
        login_count = Column(Integer)
        active = Column(Boolean())
        fs_uniquifier = Column(String(255))
        confirmed_at = Column(DateTime())
        roles = relationship('Role', secondary='roles_users',
                             backref=backref('users', lazy='dynamic'))

  Basic MongoEngine Application

  MongoEngine Install requirements

$ mkvirtualenv <your-app-name>
$ pip install flask-security-too flask-mongoengine

  MongoEngine Application

The following code sample illustrates how to get started as quickly as possible using MongoEngine:

import os
from flask import Flask, render_template
from flask_mongoengine import MongoEngine
from flask_security import Security, MongoEngineUserDatastore, \
    UserMixin, RoleMixin, auth_required, hash_password
# Create app
app = Flask(__name__)
app.config['DEBUG'] = True
# Generate a nice key using secrets.token_urlsafe()
app.config['SECRET_KEY'] = os.environ.get("SECRET_KEY", 'pf9Wkove4IKEAXvy-cQkeDPhv9Cb3Ag-wyJILbq_dFw')
# Bcrypt is set as default SECURITY_PASSWORD_HASH, which requires a salt
# Generate a good salt using: secrets.SystemRandom().getrandbits(128)
app.config['SECURITY_PASSWORD_SALT'] = os.environ.get("SECURITY_PASSWORD_SALT", '146585145368132386173505678016728509634')
# MongoDB Config
app.config['MONGODB_DB'] = 'mydatabase'
app.config['MONGODB_HOST'] = 'localhost'
app.config['MONGODB_PORT'] = 27017
# Create database connection object
db = MongoEngine(app)
class Role(db.Document, RoleMixin):
    name = db.StringField(max_length=80, unique=True)
    description = db.StringField(max_length=255)
class User(db.Document, UserMixin):
    email = db.StringField(max_length=255)
    password = db.StringField(max_length=255)
    active = db.BooleanField(default=True)
    fs_uniquifier = db.StringField(max_length=255)
    confirmed_at = db.DateTimeField()
    roles = db.ListField(db.ReferenceField(Role), default=[])
# Setup Flask-Security
user_datastore = MongoEngineUserDatastore(db, User, Role)
security = Security(app, user_datastore)
# Create a user to test with
@app.before_first_request
def create_user():
    user_datastore.create_user(email="admin@me.com", password=hash_password("password"))
# Views
@app.route("/")
@auth_required()
def home():
    return render_template('index.html')
if __name__ == '__main__':
    app.run()

  Basic Peewee Application

  Peewee Install requirements

$ mkvirtualenv <your-app-name>
$ pip install flask-security-too peewee

  Peewee Application

The following code sample illustrates how to get started as quickly as possible using Peewee:

import os
from flask import Flask, render_template
from playhouse.flask_utils import FlaskDB
from peewee import *
from flask_security import Security, PeeweeUserDatastore, \
    UserMixin, RoleMixin, auth_required, hash_password
# Create app
app = Flask(__name__)
app.config['DEBUG'] = True
# Generate a nice key using secrets.token_urlsafe()
app.config['SECRET_KEY'] = os.environ.get("SECRET_KEY", 'pf9Wkove4IKEAXvy-cQkeDPhv9Cb3Ag-wyJILbq_dFw')
# Bcrypt is set as default SECURITY_PASSWORD_HASH, which requires a salt
# Generate a good salt using: secrets.SystemRandom().getrandbits(128)
app.config['SECURITY_PASSWORD_SALT'] = os.environ.get("SECURITY_PASSWORD_SALT", '146585145368132386173505678016728509634')
app.config['DATABASE'] = {
    'name': 'example.db',
    'engine': 'peewee.SqliteDatabase',
}
# Create database connection object
db = FlaskDB(app)
class Role(db.Model, RoleMixin):
    name = CharField(unique=True)
    description = TextField(null=True)
class User(db.Model, UserMixin):
    email = TextField()
    password = TextField()
    active = BooleanField(default=True)
    fs_uniquifier = TextField()
    confirmed_at = DateTimeField(null=True)
class UserRoles(db.Model):
    # Because peewee does not come with built-in many-to-many
    # relationships, we need this intermediary class to link
    # user to roles.
    user = ForeignKeyField(User, related_name='roles')
    role = ForeignKeyField(Role, related_name='users')
    name = property(lambda self: self.role.name)
    description = property(lambda self: self.role.description)
# Setup Flask-Security
user_datastore = PeeweeUserDatastore(db, User, Role, UserRoles)
security = Security(app, user_datastore)
# Create a user to test with
@app.before_first_request
def create_user():
    for Model in (Role, User, UserRoles):
        Model.drop_table(fail_silently=True)
        Model.create_table(fail_silently=True)
    user_datastore.create_user(email="test@me.com", password=hash_password("password"))
# Views
@app.route('/')
@auth_required()
def home():
    return render_template('index.html')
if __name__ == '__main__':
    app.run()

  Mail Configuration

Flask-Security integrates with Flask-Mail to handle all email communications between user and site, so it's important to configure Flask-Mail with your email server details so Flask-Security can talk with Flask-Mail correctly.

The following code illustrates a basic setup, which could be added to the basic application code in the previous section:

# At top of file
from flask_mail import Mail
# After 'Create app'
app.config['MAIL_SERVER'] = 'smtp.example.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USERNAME'] = 'username'
app.config['MAIL_PASSWORD'] = 'password'
mail = Mail(app)

To learn more about the various Flask-Mail settings to configure it to work with your particular email server configuration, please see the Flask-Mail documentation.

  Proxy Configuration

The user tracking features need an additional configuration in HTTP proxy environment. The following code illustrates a setup with a single HTTP proxy in front of the web application:

# At top of file
from werkzeug.middleware.proxy_fix import ProxyFix
# After 'Create app'
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)

To learn more about the ProxyFix middleware, please see the Werkzeug documentation.

  Unit Testing Your Application

As soon as you add any of the Flask-Security decorators to your API endpoints, it can be frustrating to unit test your basic routing (and roles and permissions). Without getting into the argument of the difference between unit tests and integration tests - you can approach testing in 2 ways:

•   'Pure' unit test - mocking out all lower level objects (such as the data store)

•   Complete app with in-memory/temporary DB (with little or no mocking).

    Look in the Flask-Security repo examples directory for actual code that implements the first approach.

  Models

Flask-Security assumes you'll be using libraries such as SQLAlchemy, MongoEngine, Peewee or PonyORM to define a data model that includes a User and Role model. The fields on your models must follow a particular convention depending on the functionality your app requires. Aside from this, you're free to add any additional fields to your model(s) if you want.

As more features are added to Flask-Security, the requirements for required fields and tables grow. As you use these features, and therefore use these fields and tables, database migrations are required; which are a bit of a pain. To make things easier - Flask-Security includes mixins that contain ALL the fields and tables required for all features. They also contain various best practice fields - such as update and create times. These mixins can be easily extended to add any sort of custom fields and can be found in the models module (today there is just one for using Flask-SqlAlchemy).

At the bare minimum your User and Role model should include the following fields:

User

•   id

•   email

•   password

•   active

•   fs_uniquifier

    Role

•   id

•   name

•   description

  Additional Functionality

Depending on the application's configuration, additional fields may need to be added to your User model.

  Confirmable

If you enable account confirmation by setting your application's SECURITY_CONFIRMABLE configuration value to True, your User model will require the following additional field:

•   confirmed_at

  Trackable

If you enable user tracking by setting your application's SECURITY_TRACKABLE configuration value to True, your User model will require the following additional fields:

•   last_login_at

•   current_login_at

•   last_login_ip

•   current_login_ip

•   login_count

  Two_Factor

If you enable two-factor by setting your application's SECURITY_TWO_FACTOR configuration value to True, your User model will require the following additional fields:

•   tf_totp_secret

•   tf_primary_method

    If you include 'sms' in SECURITY_TWO_FACTOR_ENABLED_METHODS, your User model will require the following additional field:

•   tf_phone_number

  Permissions

If you want to protect endpoints with permissions, and assign permissions to roles that are then assigned to users the Role model requires:

•   permissions

  Custom User Payload

If you want a custom payload for JSON API responses, define the method get_security_payload in your User model. The method must return a serializable object:

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    email = TextField()
    password = TextField()
    active = BooleanField(default=True)
    confirmed_at = DateTimeField(null=True)
    name = db.Column(db.String(80))
    # Custom User Payload
    def get_security_payload(self):
        return {
            'id': self.id,
            'name': self.name,
            'email': self.email
        }

CUSTOMIZING AND USAGE PATTERNS

  Customizing

Flask-Security bootstraps your application with various views for handling its configured features to get you up and running as quickly as possible. However, you'll probably want to change the way these views look to be more in line with your application's visual design.

  Views

Flask-Security is packaged with a default template for each view it presents to a user. Templates are located within a subfolder named security. The following is a list of view templates:

•   security/forgot_password.html

•   security/login_user.html

•   security/register_user.html

•   security/reset_password.html

•   security/change_password.html

•   security/send_confirmation.html

•   security/send_login.html

•   security/two_factor_verify_password.html

•   security/two_factor_setup.html

•   security/two_factor_verify_code.html

    Overriding these templates is simple:

1.  Create a folder named security within your application's templates folder

2.  Create a template with the same name for the template you wish to override

    You can also specify custom template file paths in the configuration.

    Each template is passed a template context object that includes the following, including the objects/values that are passed to the template by the main Flask application context processor:

•   <template_name>_form: A form object for the view

•   security: The Flask-Security extension object

    To add more values to the template context, you can specify a context processor for all views or a specific view. For example:

    security = Security(app, user_datastore)
    # This processor is added to all templates
    @security.context_processor
    def security_context_processor():
        return dict(hello="world")
    # This processor is added to only the register view
    @security.register_context_processor
    def security_register_processor():
        return dict(something="else")

    The following is a list of all the available context processor decorators:

•   context_processor: All views

•   forgot_password_context_processor: Forgot password view

•   login_context_processor: Login view

•   register_context_processor: Register view

•   reset_password_context_processor: Reset password view

•   change_password_context_processor: Change password view

•   send_confirmation_context_processor: Send confirmation view

•   send_login_context_processor: Send login view

•   mail_context_processor: Whenever an email will be sent

•   tf_setup_context_processor: Two factor setup view

•   tf_token_validation_context_processor: Two factor token validation view

•   tf_verify_password_context_processor: Two factor password re-verify view

  Forms

All forms can be overridden. For each form used, you can specify a replacement class. This allows you to add extra fields to the register form or override validators:

from flask_security import RegisterForm
from wtforms import StringField
from wtforms.validators import DataRequired
class ExtendedRegisterForm(RegisterForm):
    first_name = StringField('First Name', [DataRequired()])
    last_name = StringField('Last Name', [DataRequired()])
security = Security(app, user_datastore,
         register_form=ExtendedRegisterForm)

For the register_form and confirm_register_form, each field is passed to the user model (as kwargs) when a user is created. In the above case, the first_name and last_name fields are passed directly to the model, so the model should look like:

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(255), unique=True)
    password = db.Column(db.String(255))
    first_name = db.Column(db.String(255))
    last_name = db.Column(db.String(255))

The following is a list of all the available form overrides:

•   login_form: Login form

•   confirm_register_form: Confirmable register form

•   register_form: Register form

•   forgot_password_form: Forgot password form

•   reset_password_form: Reset password form

•   change_password_form: Change password form

•   send_confirmation_form: Send confirmation form

•   passwordless_login_form: Passwordless login form

•   two_factor_verify_code_form: Two-factor verify code form

•   two_factor_setup_form: Two-factor setup form

•   two_factor_verify_password_form: Two-factor verify password form

•   two_factor_rescue_form: Two-factor help user form

  Localization

All messages, form labels, and form strings are localizable. Flask-Security uses Flask-BabelEx to manage its messages. All translations are tagged with a domain, as specified by the configuration variable SECURITY_I18N_DOMAIN (default: "security"). For messages and labels all this works seamlessly. For strings inside templates it is necessary to explicitly ask for the "security" domain, since your application itself might have its own domain. Flask-Security places the method _fsdomain in jinja2's global environment. In order to reference a Flask-Security translation from ANY template (such as if you copied and modified an existing security template) just use that method:

{{ _fsdomain("Login") }}

  Emails

Flask-Security is also packaged with a default template for each email that it may send. Templates are located within the subfolder named security/email. The following is a list of email templates:

•   security/email/confirmation_instructions.html

•   security/email/confirmation_instructions.txt

•   security/email/login_instructions.html

•   security/email/login_instructions.txt

•   security/email/reset_instructions.html

•   security/email/reset_instructions.txt

•   security/email/reset_notice.html

•   security/email/change_notice.txt

•   security/email/change_notice.html

•   security/email/reset_notice.txt

•   security/email/welcome.html

•   security/email/welcome.txt

•   security/email/two_factor_instructions.html

•   security/email/two_factor_instructions.txt

•   security/email/two_factor_rescue.html

•   security/email/two_factor_rescue.txt

    Overriding these templates is simple:

1.  Create a folder named security within your application's templates folder

2.  Create a folder named email within the security folder

3.  Create a template with the same name for the template you wish to override

    Each template is passed a template context object that includes values for any links that are required in the email. If you require more values in the templates, you can specify an email context processor with the mail_context_processor decorator. For example:

    security = Security(app, user_datastore)
    # This processor is added to all emails
    @security.mail_context_processor
    def security_mail_processor():
        return dict(hello="world")

  Emails with Celery

Sometimes it makes sense to send emails via a task queue, such as Celery. To delay the sending of emails, you can use the @security.send_mail_task decorator like so:

# Setup the task
@celery.task
def send_security_email(msg):
    # Use the Flask-Mail extension instance to send the incoming ``msg`` parameter
    # which is an instance of `flask_mail.Message`
    mail.send(msg)
@security.send_mail_task
def delay_security_email(msg):
    send_security_email.delay(msg)

If factory method is going to be used for initialization, use _SecurityState object returned by init_app method to initialize Celery tasks instead of using security.send_mail_task directly like so:

from flask import Flask
from flask_mail import Mail
from flask_security import Security, SQLAlchemyUserDatastore
from celery import Celery
mail = Mail()
security = Security()
celery = Celery()
def create_app(config):
    """Initialize Flask instance."""
    app = Flask(__name__)
    app.config.from_object(config)
    @celery.task
    def send_flask_mail(msg):
        mail.send(msg)
    mail.init_app(app)
    datastore = SQLAlchemyUserDatastore(db, User, Role)
    security_ctx = security.init_app(app, datastore)
    # Flexible way for defining custom mail sending task.
    @security_ctx.send_mail_task
    def delay_flask_security_mail(msg):
        send_flask_mail.delay(msg)
    # A shortcut.
    security_ctx.send_mail_task(send_flask_mail.delay)
    return app

Note that flask_mail.Message may not be serialized as an argument passed to Celery. The practical way with custom serialization may look like so:

@celery.task
def send_flask_mail(**kwargs):
        mail.send(Message(**kwargs))
@security_ctx.send_mail_task
def delay_flask_security_mail(msg):
    send_flask_mail.delay(subject=msg.subject, sender=msg.sender,
                          recipients=msg.recipients, body=msg.body,
                          html=msg.html)

  Custom send_mail method

It's also possible to completely override the security.send_mail method to implement your own logic.

For example, you might want to use an alternative email library like Flask-Emails:

from flask import Flask
from flask_security import Security, SQLAlchemyUserDatastore
from flask_emails import Message
def create_app(config):
    """Initialize Flask instance."""
    app = Flask(__name__)
    app.config.from_object(config)
    def custom_send_mail(subject, recipient, template, **context):
        ctx = ('security/email', template)
        message = Message(
            subject=subject,
            html=_security.render_template('%s/%s.html' % ctx, **context))
        message.send(mail_to=[recipient])
    datastore = SQLAlchemyUserDatastore(db, User, Role)
    Security(app, datastore, send_mail=custom_send_mail)
    return app

NOTE: The above security.send_mail_task override will be useless if you override the entire send_mail method.

  Responses

Flask-Security will likely be a very small piece of your application, so Flask-Security makes it easy to override all aspects of API responses.

  JSON Response

Applications that support a JSON based API need to be able to have a uniform API response. Flask-Security has a default way to render its API responses - which can be easily overridden by providing a callback function via Security.render_json(). As documented in Security.render_json(), be aware that Flask-Security registers its own JsonEncoder on its blueprint.

  401, 403, Oh My

For a very long read and discussion; look at this. Out of the box, Flask-Security in tandem with Flask-Login, behaves as follows:

•   If authentication fails as the result of a @login_required, @auth_required, @http_auth_required, or @token_auth_required then if the request 'wants' a JSON response, Security.render_json() is called with a 401 status code. If not then flask_login.LoginManager.unauthorized() is called. By default THAT will redirect to a login view.

•   If authorization fails as the result of @roles_required, @roles_accepted, @permissions_required, or @permissions_accepted, then if the request 'wants' a JSON response, Security.render_json() is called with a 403 status code. If not, then if SECURITY_UNAUTHORIZED_VIEW is defined, the response will redirected. If SECURITY_UNAUTHORIZED_VIEW is not defined, then abort(403) is called.

    All this can be easily changed by registering any or all of Security.render_json(), Security.unauthn_handler() and Security.unauthz_handler().

    The decision on whether to return JSON is based on:

•   Was the request content-type "application/json" (e.g. request.is_json()) OR

•   Is the 'best' value of the Accept HTTP header "application/json"

  Authorization with OAuth2

Flask-Security can be set up to co-operate with Flask-OAuthlib, by implementing a custom request loader that authorizes a user based either on a Bearer token in the HTTP Authorization header, or on the Flask-Security standard authorization logic:

from flask_oauthlib.provider import OAuth2Provider
from flask_security import AnonymousUser
from flask_security.core import (
    _user_loader as _flask_security_user_loader,
    _request_loader as _flask_security_request_loader)
from flask_security.utils import config_value as security_config_value
oauth = OAuth2Provider(app)
def _request_loader(request):
    """
    Load user from OAuth2 Authentication header or using
    Flask-Security's request loader.
    """
    user = None
    if hasattr(request, 'oauth'):
        user = request.oauth.user
    else:
        # Need this try stmt in case oauthlib sometimes throws:
        # AttributeError: dict object has no attribute startswith
        try:
            is_valid, oauth_request = oauth.verify_request(scopes=[])
            if is_valid:
                user = oauth_request.user
        except AttributeError:
            pass
    if not user:
        user = _flask_security_request_loader(request)
    return user
def _get_login_manager(app, anonymous_user):
    """Prepare a login manager for Flask-Security to use."""
    login_manager = LoginManager()
    login_manager.anonymous_user = anonymous_user or AnonymousUser
    login_manager.login_view = '{0}.login'.format(
        security_config_value('BLUEPRINT_NAME', app=app))
    login_manager.user_loader(_flask_security_user_loader)
    login_manager.request_loader(_request_loader)
    if security_config_value('FLASH_MESSAGES', app=app):
        (login_manager.login_message,
         login_manager.login_message_category) = (
            security_config_value('MSG_LOGIN', app=app))
        (login_manager.needs_refresh_message,
         login_manager.needs_refresh_message_category) = (
            security_config_value('MSG_REFRESH', app=app))
    else:
        login_manager.login_message = None
        login_manager.needs_refresh_message = None
    login_manager.init_app(app)
    return login_manager
security = Security(
    app, user_datastore,
    login_manager=_get_login_manager(app, anonymous_user=None))

  Two-factor Configurations

Two-factor authentication provides a second layer of security to any type of login, requiring extra information or a secondary device to log in, in addition to ones login credentials. The added feature includes the ability to add a secondary authentication method using either via email, sms message, or Google Authenticator.

The following code sample illustrates how to get started as quickly as possible using SQLAlchemy and two-factor feature:

•   Basic SQLAlchemy Application

  Basic SQLAlchemy Two-Factor Application

  SQLAlchemy Install requirements

$ mkvirtualenv <your-app-name>
$ pip install flask-security-too flask-sqlalchemy cryptography pyqrcode

  Two-factor Application

The following code sample illustrates how to get started as quickly as possible using SQLAlchemy:

from flask import Flask, current_app, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_security import Security, SQLAlchemyUserDatastore, \
    UserMixin, RoleMixin, login_required
# At top of file
from flask_mail import Mail
# Convenient references
from werkzeug.datastructures import MultiDict
from werkzeug.local import LocalProxy
_security = LocalProxy(lambda: current_app.extensions['security'])
_datastore = LocalProxy(lambda: _security.datastore)
# Create app
app = Flask(__name__)
app.config['DEBUG'] = True
# Generate a nice key using secrets.token_urlsafe()
app.config['SECRET_KEY'] = os.environ.get("SECRET_KEY", 'pf9Wkove4IKEAXvy-cQkeDPhv9Cb3Ag-wyJILbq_dFw')
# Bcrypt is set as default SECURITY_PASSWORD_HASH, which requires a salt
# Generate a good salt using: secrets.SystemRandom().getrandbits(128)
app.config['SECURITY_PASSWORD_SALT'] = os.environ.get("SECURITY_PASSWORD_SALT", '146585145368132386173505678016728509634')
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
app.config['SECURITY_TWO_FACTOR_ENABLED_METHODS'] = ['mail',
  'google_authenticator']  # 'sms' also valid but requires an sms provider
app.config['SECURITY_TWO_FACTOR'] = True
# Generate a good totp secret using: passlib.totp.generate_secret()
app.config['SECURITY_TWO_FACTOR_SECRET'] = {"1": "TjQ9Qa31VOrfEzuPy4VHQWPCTmRzCnFzMKLxXYiZu9B"}
app.config['SECURITY_TWO_FACTOR_RESCUE_MAIL'] = 'put_your_mail@gmail.com'
app.config['SECURITY_TWO_FACTOR_URI_SERVICE_NAME'] = 'put_your_app_name'
# Create database connection object
db = SQLAlchemy(app)
# Define models
roles_users = db.Table('roles_users',
    db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
    db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))
class Role(db.Model, RoleMixin):
  id = db.Column(db.Integer(), primary_key=True)
  name = db.Column(db.String(80), unique=True)
  description = db.Column(db.String(255))
class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(255), unique=True)
    password = db.Column(db.String(255))
    active = db.Column(db.Boolean())
    confirmed_at = db.Column(db.DateTime())
    roles = db.relationship('Role', secondary=roles_users,
                            backref=db.backref('users', lazy='dynamic'))
    tf_phone_number = db.Column(db.String(64))
    tf_primary_method = db.Column(db.String(140))
    tf_totp_secret = db.Column(db.String(255))
# Setup Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
mail = Mail(app)
# Create a user to test with
@app.before_first_request
def create_user():
    db.create_all()
    user_datastore.create_user(email='gal@lp.com', password='password', username='gal',
                           tf_totp_secret=None, tf_primary_method=None)
    db.session.commit()
# Views
@app.route('/')
@login_required
def home():
    return render_template('index.html')
if __name__ == '__main__':
    app.run()

  Working with Single Page Applications

Single Page Applications (spa) are a popular model for both separating user interface from application/backend code as well as providing a responsive user experience. Angular and Vue are popular Javascript frameworks for writing SPAs. An added benefit is that the UI can be developed completely independently (in a separate repo) and take advantage of the latest Javascript packing and bundling technologies that are evolving rapidly, and not make the Flask application have to deal with things like Flask-Webpack or webassets.

For the purposes of this application note - this implies:

•   The user interface code is delivered by some other means than the Flask application. In particular this means that there are no opportunities to inject context/environment via a templating language.

•   The user interface interacts with the backend Flask application via JSON requests and responses - not forms.

•   SPAs are still browser based - so they have the same security vulnerabilities as traditional html/form-based applications.

•   SPAs handle all routing/redirection via code, so redirects need context.

  Configuration

An example configuration:

# no forms so no concept of flashing
SECURITY_FLASH_MESSAGES = False
# Need to be able to route backend flask API calls. Use 'accounts'
# to be the Flask-Security endpoints.
SECURITY_URL_PREFIX = '/api/accounts'
# Turn on all the great Flask-Security features
SECURITY_RECOVERABLE = True
SECURITY_TRACKABLE = True
SECURITY_CHANGEABLE = True
SECURITY_CONFIRMABLE = True
SECURITY_REGISTERABLE = True
# These need to be defined to handle redirects
# As defined in the API documentation - they will receive the relevant context
SECURITY_POST_CONFIRM_VIEW = '/confirmed'
SECURITY_CONFIRM_ERROR_VIEW = '/confirm-error
SECURITY_RESET_VIEW = '/reset-password'
SECURITY_RESET_ERROR_VIEW = '/reset-password'
SECURITY_REDIRECT_BEHAVIOR = 'spa'
# CSRF protection is critical for all session-based browser UIs
# enforce CSRF protection for session / browser - but allow token-based
# API calls to go through
SECURITY_CSRF_PROTECT_MECHANISMS = ["session", "basic"]
SECURITY_CSRF_IGNORE_UNAUTH_ENDPOINTS = True
# Send Cookie with csrf-token. This is the default for Axios and Angular.
SECURITY_CSRF_COOKIE = {"key": "XSRF-TOKEN"}
WTF_CSRF_CHECK_DEFAULT = False
WTF_CSRF_TIME_LIMIT = None
# In your app
# Enable CSRF on all api endpoints.
flask_wtf.CSRFProtect(app)
# Initialize Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
# Optionally define and set unauthorized callbacks
security.unauthz_handler(<your unauth handler>)

When in development mode, the Flask application will run by default on port 5000. The UI might want to run on port 8080. In order to test redirects you need to set:

SECURITY_REDIRECT_HOST = 'localhost:8080'

  Client side authentication options

Depending on your SPA architecture and vision you can choose between cookie or token based authentication.

For both there is more documentation and some examples. In both cases, you need to understand and handle csrftopic concerns.

  Security Considerations

Static elements such as your UI should be served with an industrial-grade web server - such as Nginx. This is also where various security measures should be handled such as injecting standard security headers such as:

•   Strict-Transport-Security

•   X-Frame-Options

•   Content Security Policy

•   X-Content-Type-Options

•   X-XSS-Protection

•   Referrer policy

    There are a lot of different ways to host a SPA as the javascript part itself is quit easily hosted from any static webserver. A couple of deployment options and their configurations will be describer here.

  Nginx

When serving a SPA from a Nginx webserver the Flask backend, with Flask-Security-Too, will probably be served via Nginx's reverse proxy feature. The javascript is served from Nginx itself and all calls to a certain path will be routed to the reversed proxy. The example below routes all http requests to "/api/" to the Flask backend and handles all other requests directly from javascript. This has a couple of benefits as all the requests happen within the same domain so you don't have to worry about CORS problems:

server {
    listen       80;
    server_name  www.example.com;
    #access_log  /var/log/nginx/host.access.log  main;
    root   /usr/share/nginx/html;
    index index.html;
    location / {
        try_files $uri $uri/ /index.html;
    }
    # Location of assets folder
    location ~ ^/(static)/  {
        gzip_static on;
        gzip_types text/plain text/xml text/css text/comma-separated-values
            text/javascript application/x-javascript application/atom+xml;
        expires max;
    }
    # redirect server error pages to the static page /50x.html
    # 400 error's will be handled from the SPA
    error_page   500 502 503 504  /50x.html;
        location = /50x.html {
    }
    # route all api requests to the flask app, served by gunicorn
    location /api/ {
        proxy_pass http://localhost:8080/api/;
    }
    # OR served via uwsgi
    location /api/ {
        include ..../uwsgi_params;
        uwsgi_pass unix:/tmp/uwsgi.sock;
        uwsgi_pass_header AUTHENTICATION-TOKEN;
    }
}

NOTE: The example doesn't include SSL setup to keep it simple and still suitable for a more complex kubernetes setup where Nginx is often used as a load balancer and another Nginx with SSL setup runs in front of it.

  Amazon lambda gateway / Serverless

Most Flask apps can be deployed to Amazon's lambda gateway without much hassle by using Zappa. You'll get automatic horizontal scaling, seamless upgrades, automatic SSL certificate renewal and a very cheap way of hosting a backend without being responsible for any infrastructure. Depending on how you design your app you could choose to host your backend from an api specific domain: e.g. api.example.com. When your SPA deployment structure is capable of routing the AJAX/XHR request from your javascript app to the separate backend; use it. When you want to use the backend from another e.g. www.example.com you have some deal with some CORS setup as your browser will block cross-domain POST requests. There is a Flask package for that: Flask-CORS.

The setup of CORS is simple:

CORS(
    app,
    supports_credentials=True,  # needed for cross domain cookie support
    resources="/*",
    allow_headers="*",
    origins="https://www.example.com",
    expose_headers="Authorization,Content-Type,Authentication-Token,XSRF-TOKEN",
)

You can then host your javascript app from an S3 bucket, with or without Cloudfront, GH-pages or from any static webserver.

Some background material:

•   Specific to S3 but easily adaptable.

•   Flask-Talisman - useful if serving everything from your Flask application - also useful as a good list of things to consider.

  Security Patterns

  Authentication and Authorization

Flask-Security provides a set of authentication decorators:

•   @auth_required

•   @http_auth_required

•   @token_auth_required

    and a set of authorization decorators:

•   @roles_required

•   @roles_accepted

•   @permissions_required

•   @permissions_accepted

    In addition, Flask-Login provides @login_required. In order to take advantage of all the Flask-Security features, it is recommended to NOT use @login_required.

    Also, if you annotate your endpoints with JUST an authorization decorator, you will never get a 401 response, and (for forms) you won't be redirected to your login page. In this case you will always get a 403 status code (assuming you don't override the default handlers).

    While these annotations are quick and easy, it is likely that they won't completely satisfy all an application's authorization requirements. A common example might be that a user can only edit their own posts/documents. In cases like this - it is nice to have a uniform way of handling all authorization errors. A simple way to do this is to use a special exception class that you can raise either in response to Flask-Security authorization failures, or in your own code. Then use Flask's errorhandler to catch that exception and create the appropriate API response:

    Class MyForbiddenException(Exception):
        def __init__(self, msg='Not permitted with your privileges', status=http.HTTPStatus.FORBIDDEN):
            self.info = {'status': status, 'msgs': [msg]}
    _security = app.extensions["security"]
    @_security.unauthz_handler
    def my_unauthz_handler(func, params):
        raise MyForbiddenException()
    @app.errorhandler(MyForbiddenException)
    def my_exception(ex):
        return flask.jsonify(ex.info), ex.info['status']
    @app.route('/doc/<int:doc_id>', methods=['PATCH'])
    @auth_required('token', 'session')
    def doc_patch(doc_id):
        doc = fetch_doc(doc_id)
        if not current_user.has_role('admin') and doc.owner != current_user:
            raise MyForbiddenException(msg='You can only update docs you own')

  CSRF

By default, Flask-Security, via Flask-WTForms protects all form based POSTS from CSRF attacks using well vetted per-session hidden-form-field csrf-tokens.

Any web application that relies on session cookies for authentication must have CSRF protection. For more details please read this OWASP cheatsheet. A couple important take-aways - first - it isn't about forms versus JSON - it is about how the API is authenticated (session cookies versus authentication token). Second there is the concern about 'login CSRF' - is protection needed prior to authentication (yes if you have a really secure/popular site).

Flask-Security strives to support various options for both its endpoints (e.g. /login) and the application endpoints (protected with Flask-Security decorators such as @auth_required).

If your application just uses forms that are derived from Flask-WTF::Flaskform - you are done.

  CSRF: Single-Page-Applications and AJAX/XHR

If you are thinking about using authentication tokens in your browser-based UI - read This article on how and where to store authentication tokens. While the article is talking about JWT it applies to flask_security tokens as well.

In general, it is considered more secure (and easier) to use sessions for browser based UI, and tokens for service to service and scripts.

For SPA, and especially those that aren't served via your flask application, there are difficulties with actually retrieving and using a CSRF token. There are 2 normal ways to do this:

•   Have the csrf-token available via a JSON GET request that can be attached as a header in every mutating request.

•   Have a cookie that can be read via javascript whose value is the csrf-token that can be attached as a header in every mutating request.

    Flask-Security supports both solutions.

  Explicit fetch and send of csrf-token

The current session CSRF token is returned on every JSON GET request (to a Flask-Security endpoint) as response['csrf_token`]. For web applications that ARE served via flask, it is even easier to get the csrf-token - https://flask-wtf.readthedocs.io/en/stable/csrf.html gives some useful tips.

Armed with the csrf-token, the UI must include that in every mutating operation. Be careful NOT to include the csrf-token in non-mutating requests (such as GETs). If your application uses GET to actually modify state - please stop.

An example using axios

# This will fetch the csrf-token. Note that we do a GET on the login endpoint
# which will get us the csrf-token even though we aren't yet logged in.
# Note further the 'data: null' and explicit Content-Type header - these are
# critical, otherwise Flask-Security will return the login form.
axios.get('/login',{data: null, headers: {'Content-Type': 'application/json'}}).then(function (resp) {
  csrf_token = resp.data['response']['csrf_token']
})
# This will add the token header to each outgoing mutating request.
axios.interceptors.request.use(function (config) {
  if (["post", "delete", "patch", "put"].includes(config["method"])) {
    if (csrf_token !== '') {
      config.headers["X-CSRF-Token"] = csrf_token
    }
  }
  return config;
}, function (error) {
  // Do something with request error
  return Promise.reject(error);
});

Note that we use the header name X-CSRF-Token as that is one of the default headers configured in Flask-WTF (WTF_CSRF_HEADERS)

To protect your application's endpoints (that presumably are not using Flask forms), you need to enable CSRF as described in the FlaskWTF documentation:

flask_wtf.CSRFProtect(app)

This will turn on CSRF protection on ALL endpoints, including Flask-Security. This protection differs slightly from the default that is part of FlaskForm in that it will first look at the request body and see if it can find a form field that contains the csrf-token, and if it can't, it will check if the request has a header that is listed in WTF_CSRF_HEADERS and use that. Be aware that if you enable this it will ONLY work if you send the session cookie on each request.

  Using a Cookie

You can instruct Flask-Security to send a cookie that contains the csrf token. This can be very convenient since various javascript AJAX packages are pre-configured to extract the contents of a cookie and send it on every mutating request as an HTTP header. axios for example has a default configuration that it will look for a cookie named XSRF-TOKEN and will send the contents of that back in an HTTP header called X-XSRF-Token. This means that if you use that package you don't need to make any changes to your UI and just need the following configuration:

# Have cookie sent
app.config["SECURITY_CSRF_COOKIE"] = {"key": "XSRF-TOKEN"}
# Don't have csrf tokens expire (they are invalid after logout)
app.config["WTF_CSRF_TIME_LIMIT"] = None
# You can't get the cookie until you are logged in.
app.config["SECURITY_CSRF_IGNORE_UNAUTH_ENDPOINTS"] = True
# Enable CSRF protection
flask_wtf.CSRFProtect(app)

Angular's httpClient also supports this.

For React based project you are free to choose your http client. It bundles fetch though. Retrieving the token is easy:

fetch(url, {
  credentials: 'include',
  mode: 'cors',
  headers: {
    'Accept': 'application/json',
    'X-XSRF-TOKEN': getCookieValue('XSRF-TOKEN')
  }
});

Sending the token on every, mutating, request is something that you should implement yourself. As an example an API call to an API endpoint that does CSRF validation:

function addUser(details) {
  return fetch('https://api.example.com/user', {
    mode: 'cors',
    method: 'POST',
    credentials: 'include',
    body: JSON.stringify(details),
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'X-XSRF-TOKEN': getCookieValue('XSRF-TOKEN')
    }
  }).then(response => {
    return response.json().then(data => {
      if (response.ok) {
        return data;
      } else {
        return Promise.reject({status: response.status, data});
      }
    });
  });
}

When you have axios setup correctly, this is a lot easier:

function addUser(details) {
  return axios.post('https://api.example.com/user', details);
}

  CSRF: Enable protection for session auth, but not token auth

As mentioned above, CSRF is critical for any mutating operation where the authentication credentials are 'invisibly' sent - such as a session cookie - from a browser. But if your endpoint a) can only be authenticated with an attached token or b) can be called either via session OR token; it is often desirable not to force token API users to deal with CSRF. To solve this, we need to keep CSRFProtect from checking the csrf-token early in the request and instead defer that decision to later decorators/code. Flask-Security's authentication decorators (auth_required, auth_token_required, and http_auth_required all support calling csrf protection based on configuration:

# Disable pre-request CSRF
app.config[WTF_CSRF_CHECK_DEFAULT] = False
# Check csrf for session and http auth (but not token)
app.config[SECURITY_CSRF_PROTECT_MECHANISMS] = ["session", "basic"]
# Enable CSRF protection
flask_wtf.CSRFProtect(app)
@app.route("/")
@auth_required("token", "session")
def home_page():

With this configuration, CSRF won't be required if the caller uses an authentication token, but if it uses the session cookie it will.

  CSRF: Pro-Tips

1.  Be aware that for CSRF to work, callers MUST send the session cookie. So for pure API (token based), and no session cookie - there is no way to support 'login CSRF'. So your app must set SECURITY_CSRF_IGNORE_UNAUTH_ENDPOINTS (or clients must use CSRF/session cookie for logging in then once they have an authentication token, no further need for cookie).

2.  If you enable CSRFProtect(app) and you want to support non-form based JSON requests, then you must include the CSRF token in the header (e.g. X-CSRF-Token)

3.  You must enable CSRFProtect(app) if you want to accept the CSRF token in the request header.

4.  Annotate each of your endpoints with a @auth_required decorator (and don't rely on just a @role_required or @login_required decorator) so that Flask-Security gets control at the appropriate place.

5.  If you can't use a decorator, Flask-Security exposes the underlying method flask_security.handle_csrf().

6.  Consider starting by setting SECURITY_CSRF_IGNORE_UNAUTH_ENDPOINTS to True. Your application likely doesn't need 'login CSRF' protection, and it is frustrating to not even be able to login via API!

7.  If you have unauthenticated endpoints that you want to protect with CSRF then use the flask_security.unauth_csrf() decorator.

API

  API

The external (json/form) API is described here

  Core

class flask_security.Security(app=None, datastore=None, register_blueprint=True, **kwargs)
    The Security class initializes the Flask-Security extension.

Parameters

•   app -- The application.

•   datastore -- An instance of a user datastore.

•   register_blueprint -- to register the Security blueprint or not.

•   login_form -- set form for the login view

•   register_form -- set form for the register view when SECURITY_CONFIRMABLE is false

•   confirm_register_form -- set form for the register view when SECURITY_CONFIRMABLE is true

•   forgot_password_form -- set form for the forgot password view

•   reset_password_form -- set form for the reset password view

•   change_password_form -- set form for the change password view

•   send_confirmation_form -- set form for the send confirmation view

•   passwordless_login_form -- set form for the passwordless login view

•   two_factor_setup_form -- set form for the 2FA setup view

•   two_factor_verify_code_form -- set form the the 2FA verify code view

•   two_factor_rescue_form -- set form for the 2FA rescue view

•   two_factor_verify_password_form -- set form for the 2FA verify password view

•   anonymous_user -- class to use for anonymous user

•   render_template -- function to use to render templates. The default is Flask's render_template() function.

•   send_mail -- function to use to send email. Defaults to send_mail()

•   json_encoder_cls -- Class to use as blueprint.json_encoder. Defaults to FsJsonEncoder

init_app(app, datastore=None, register_blueprint=None, **kwargs)
    Initializes the Flask-Security extension for the specified application and datastore implementation.

Parameters

•   app -- The application.

•   datastore -- An instance of a user datastore.

•   register_blueprint -- to register the Security blueprint or not.

render_json(cb)
    Callback to render response payload as JSON.

Parameters
    cb --

    Callback function with signature (payload, code, headers=None, user=None)

payload
    A dict. Please see the formal API spec for details.

code
    Http status code

headers
    Headers object

user
    the UserDatastore object (or None). Note that this is usually the same as current_user - but not always.

    The default implementation simply returns:

    headers["Content-Type"] = "application/json"
    payload = dict(meta=dict(code=code), response=payload)
    return make_response(jsonify(payload), code, headers)

    IMPORTANT: Be aware the Flask's jsonify method will first look to see if a json_encoder has been set on the blueprint corresponding to the current request. If not then it looks for a json_encoder registered on the app; and finally uses Flask's default JSONEncoder class. Flask-Security registers FsJsonEncoder() as its blueprint json_encoder.

    This can be used by applications to unify all their JSON API responses. This is called in a request context and should return a Response or something Flask can create a Response from.

    New in version 3.3.0.

unauthn_handler(cb)
    Callback for failed authentication. This is called by auth_required(), auth_token_required() or http_auth_required() if authentication fails.

Parameters
    cb --

    Callback function with signature (mechanisms, headers=None)

mechanisms
    List of which authentication mechanisms were tried

headers
    dict of headers to return

    Should return a Response or something Flask can create a Response from. Can raise an exception if it is handled as part of flask.errorhandler(<exception>)

    The default implementation will return a 401 response if the request was JSON, otherwise lets flask_login.login_manager.unauthorized() handle redirects.

    New in version 3.3.0.

unauthz_handler(cb)
    Callback for failed authorization. This is called by the roles_required(), roles_accepted(), permissions_required(), or permissions_accepted() if a role or permission is missing.

Parameters
    cb --

    Callback function with signature (func, params)

func
    the decorator function (e.g. roles_required)

params
    list of what (if any) was passed to the decorator.

    Should return a Response or something Flask can create a Response from. Can raise an exception if it is handled as part of flask.errorhandler(<exception>)

    With the passed parameters the application could deliver a concise error message.

    New in version 3.3.0.

want_json(fn)
    Function that returns True if response should be JSON (based on the request)

Parameters
    fn --

    Function with the following signature (request)

request
    Werkzueg/Flask request

    The default implementation returns True if either the Content-Type is "application/json" or the best Accept header value is "application/json".

    New in version 3.3.0.

flask_security.current_user
    A proxy for the current user.

flask_security.Security.unauthorized_handler()
    If an endpoint fails authentication or authorization from one of the decorators described below (except login_required), a method annotated with this decorator will be called. For login_required (which is implemented in Flask-Login) use flask_security.login_manager.unauthorized_handler

    Deprecated since version 3.3.0.

  Protecting Views

flask_security.anonymous_user_required(f)
    Decorator which requires that caller NOT be logged in. If a logged in user accesses an endpoint protected with this decorator they will be redirected to the SECURITY_POST_LOGIN_VIEW. If the caller requests a JSON response, a 400 will be returned.

    Changed in version 3.3.0: Support for JSON response was added.

flask_security.http_auth_required(realm)
    Decorator that protects endpoints using Basic HTTP authentication.

Parameters
    realm -- optional realm name

    Once authenticated, if so configured, CSRF protection will be tested.

flask_security.auth_token_required(fn)
    Decorator that protects endpoints using token authentication. The token should be added to the request by the client by using a query string variable with a name equal to the configuration value of SECURITY_TOKEN_AUTHENTICATION_KEY or in a request header named that of the configuration value of SECURITY_TOKEN_AUTHENTICATION_HEADER

    Once authenticated, if so configured, CSRF protection will be tested.

flask_security.auth_required(*auth_methods)
    Decorator that protects endpoints through multiple mechanisms Example:

    @app.route('/dashboard')
    @auth_required('token', 'session')
    def dashboard():
        return 'Dashboard'

Parameters
    auth_methods -- Specified mechanisms (token, basic, session). If not specified then all current available mechanisms will be tried.

    Note that regardless of order specified - they will be tried in the following order: token, session, basic.

    The first mechanism that succeeds is used, following that, depending on configuration, CSRF protection will be tested.

    On authentication failure Security.unauthorized_callback() (deprecated) or Security.unauthn_handler() will be called.

    Changed in version 3.3.0: If auth_methods isn't specified, then all will be tried. Authentication mechanisms will always be tried in order of token, session, basic regardless of how they are specified in the auth_methods parameter.

flask_security.login_required(func)
    If you decorate a view with this, it will ensure that the current user is logged in and authenticated before calling the actual view. (If they are not, it calls the LoginManager.unauthorized callback.) For example:

    @app.route('/post')
    @login_required
    def post():
        pass

    If there are only certain times you need to require that your user is logged in, you can do so with:

    if not current_user.is_authenticated:
        return current_app.login_manager.unauthorized()

    ...which is essentially the code that this function adds to your views.

    It can be convenient to globally turn off authentication when unit testing. To enable this, if the application configuration variable LOGIN_DISABLED is set to True, this decorator will be ignored.

    NOTE: Per W3 guidelines for CORS preflight requests, HTTP OPTIONS requests are exempt from login checks.

Parameters
    func (function) -- The view function to decorate.

flask_security.roles_required(*roles)
    Decorator which specifies that a user must have all the specified roles. Example:

    @app.route('/dashboard')
    @roles_required('admin', 'editor')
    def dashboard():
        return 'Dashboard'

    The current user must have both the admin role and editor role in order to view the page.

Parameters
    roles -- The required roles.

flask_security.roles_accepted(*roles)
    Decorator which specifies that a user must have at least one of the specified roles. Example:

    @app.route('/create_post')
    @roles_accepted('editor', 'author')
    def create_post():
        return 'Create Post'

    The current user must have either the editor role or author role in order to view the page.

Parameters
    roles -- The possible roles.

flask_security.permissions_required(*fsperms)
    Decorator which specifies that a user must have all the specified permissions. Example:

    @app.route('/dashboard')
    @permissions_required('admin-write', 'editor-write')
    def dashboard():
        return 'Dashboard'

    The current user must have BOTH permissions (via the roles it has) to view the page.

    N.B. Don't confuse these permissions with flask-principle Permission()!

Parameters
    fsperms -- The required permissions.

    New in version 3.3.0.

flask_security.permissions_accepted(*fsperms)
    Decorator which specifies that a user must have at least one of the specified permissions. Example:

    @app.route('/create_post')
    @permissions_accepted('editor-write', 'author-wrote')
    def create_post():
        return 'Create Post'

    The current user must have one of the permissions (via the roles it has) to view the page.

    N.B. Don't confuse these permissions with flask-principle Permission()!

Parameters
    fsperms -- The possible permissions.

    New in version 3.3.0.

flask_security.unauth_csrf(fall_through=False)
    Decorator for endpoints that don't need authentication but do want CSRF checks (available via Header rather than just form). This is required when setting WTF_CSRF_CHECK_DEFAULT = False since in that case, without this decorator, the form validation will attempt to do the CSRF check, and that will fail since the csrf-token is in the header (for pure JSON requests).

    This decorator does nothing unless Flask-WTF::CSRFProtect has been initialized.

    This decorator does nothing if WTF_CSRF_ENABLED == False.

    This decorator will always require CSRF if the caller is authenticated.

    This decorator will suppress CSRF if caller isn't authenticated and has set the SECURITY_CSRF_IGNORE_UNAUTH_ENDPOINTS config variable.

Parameters
    fall_through -- if set to True, then if CSRF fails here - simply keep going. This is appropriate if underlying view is form based and once the form is instantiated, the csrf_token will be available. Note that this can mask some errors such as 'The CSRF session token is missing.' meaning that the caller didn't send a session cookie and instead the caller might get a 'The CSRF token is missing.' error.

    New in version 3.3.0.

flask_security.handle_csrf(method)
    Invoke CSRF protection based on authentication method.

    Usually this is called as part of a decorator, but if that isn't appropriate, endpoint code can call this directly.

    If CSRF protection is appropriate, this will call flask_wtf::protect() which will raise a ValidationError on CSRF failure.

    This routine does nothing if any of these are true:

1.  WTF_CSRF_ENABLED is set to False

2.  the Flask-WTF CSRF module hasn't been initialized

3.  csrfProtect already checked and accepted the token

    If the passed in method is not in SECURITY_CSRF_PROTECT_MECHANISMS then not only will no CSRF code be run, but a flag in the current context fs_ignore_csrf will be set so that downstream code knows to ignore any CSRF checks.

    New in version 3.3.0.

  User Object Helpers

class flask_security.UserMixin
    Mixin for User model definitions

get_auth_token()
    Constructs the user's authentication token.

    This data MUST be securely signed using the remember_token_serializer

get_redirect_qparams(existing=None)
    Return user info that will be added to redirect query params.

    New in version 3.2.0.

Parameters
    existing -- A dict that will be updated.

Returns
    A dict whose keys will be query params and values will be query values.

get_security_payload()
    Serialize user object as response payload.

has_permission(permission)
    Returns True if user has this permission (via a role it has).

Parameters
    permission -- permission string name

    New in version 3.3.0.

has_role(role)
    Returns True if the user identifies with the specified role.

Parameters
    role -- A role name or Role instance

property is_active
    Returns True if the user is active.

verify_and_update_password(password)
    Returns True if the password is valid for the specified user.

    Additionally, the hashed password in the database is updated if the hashing algorithm happens to have changed.

    N.B. you MUST call DB commit if you are using a session-based datastore (such as SqlAlchemy) since the user instance might have been altered (i.e. app.security.datastore.commit()). This is usually handled in the view.

    New in version 3.2.0.

Parameters
    password -- A plaintext password to verify

verify_auth_token(data)
    Perform additional verification of contents of auth token. Prior to this being called the token has been validated (via signing) and has not expired.

Parameters
    data -- the data as formulated by get_auth_token()

    New in version 3.3.0.

class flask_security.RoleMixin
    Mixin for Role model definitions

add_permissions(permissions)
    Add one or more permissions to role.

Parameters
    permissions -- a set, list, or single string.

    Caller must commit to DB.

    New in version 3.3.0.

get_permissions()
    Return set of permissions associated with role.

    New in version 3.3.0.

remove_permissions(permissions)
    Remove one or more permissions from role.

Parameters
    permissions -- a set, list, or single string.

    Caller must commit to DB.

    New in version 3.3.0.

class flask_security.AnonymousUser
    AnonymousUser definition

has_role(*args)
    Returns False

  Datastores

class flask_security.UserDatastore(user_model, role_model)
    Abstracted user datastore.

Parameters

•   user_model -- A user model class definition

•   role_model -- A role model class definition

    Be aware that for mutating operations, the user/role will be added to the datastore (by calling self.put(<object>). If the datastore is session based (such as for SQLAlchemyDatastore) it is up to caller to actually commit the transaction by calling datastore.commit().

activate_user(user)
    Activates a specified user. Returns True if a change was made.

Parameters
    user -- The user to activate

add_role_to_user(user, role)
    Adds a role to a user.

Parameters

•   user -- The user to manipulate. Can be an User object or email

•   role -- The role to add to the user. Can be a Role object or string role name

create_role(**kwargs)
    Creates and returns a new role from the given parameters. Supported params (depending on RoleModel):

Parameters

•   name -- Role name

•   permissions --

    a comma delimited list of permissions, a set or a list. These are user-defined strings that correspond to strings used with @permissions_required()

    New in version 3.3.0.

create_user(**kwargs)
    Creates and returns a new user from the given parameters.

Parameters

•   email -- required.

•   password -- Hashed password.

•   roles -- list of roles to be added to user. Can be Role objects or strings

    DANGER: Be aware that whatever password is passed in will be stored directly in the DB. Do NOT pass in a plaintext password! Best practice is to pass in hash_password(plaintext_password).

    The new user's active property will be set to true.

deactivate_user(user)
    Deactivates a specified user. Returns True if a change was made.

    This will immediately disallow access to all endpoints that require authentication either via session or tokens. The user will not be able to log in again.

Parameters
    user -- The user to deactivate

delete_user(user)
    Deletes the specified user.

Parameters
    user -- The user to delete

find_or_create_role(name, **kwargs)
    Returns a role matching the given name or creates it with any additionally provided parameters.

find_role(*args, **kwargs)
    Returns a role matching the provided name.

find_user(*args, **kwargs)
    Returns a user matching the provided parameters.

get_user(id_or_email)
    Returns a user matching the specified ID or email address.

remove_role_from_user(user, role)
    Removes a role from a user.

Parameters

•   user -- The user to manipulate. Can be an User object or email

•   role -- The role to remove from the user. Can be a Role object or string role name

set_uniquifier(user, uniquifier=None)
    Set user's authentication token uniquifier. This will immediately render outstanding auth tokens invalid.

Parameters

•   user -- User to modify

•   uniquifier -- Unique value - if none then uuid.uuid4().hex is used

    This method is a no-op if the user model doesn't contain the attribute fs_uniquifier

    New in version 3.3.0.

toggle_active(user)
    Toggles a user's active status. Always returns True.

class flask_security.SQLAlchemyUserDatastore(db, user_model, role_model)
    A SQLAlchemy datastore implementation for Flask-Security that assumes the use of the Flask-SQLAlchemy extension.

activate_user(user)
    Activates a specified user. Returns True if a change was made.

Parameters
    user -- The user to activate

add_role_to_user(user, role)
    Adds a role to a user.

Parameters

•   user -- The user to manipulate. Can be an User object or email

•   role -- The role to add to the user. Can be a Role object or string role name

create_role(**kwargs)
    Creates and returns a new role from the given parameters. Supported params (depending on RoleModel):

Parameters

•   name -- Role name

•   permissions --

    a comma delimited list of permissions, a set or a list. These are user-defined strings that correspond to strings used with @permissions_required()

    New in version 3.3.0.

create_user(**kwargs)
    Creates and returns a new user from the given parameters.

Parameters

•   email -- required.

•   password -- Hashed password.

•   roles -- list of roles to be added to user. Can be Role objects or strings

    DANGER: Be aware that whatever password is passed in will be stored directly in the DB. Do NOT pass in a plaintext password! Best practice is to pass in hash_password(plaintext_password).

    The new user's active property will be set to true.

deactivate_user(user)
    Deactivates a specified user. Returns True if a change was made.

    This will immediately disallow access to all endpoints that require authentication either via session or tokens. The user will not be able to log in again.

Parameters
    user -- The user to deactivate

delete_user(user)
    Deletes the specified user.

Parameters
    user -- The user to delete

find_or_create_role(name, **kwargs)
    Returns a role matching the given name or creates it with any additionally provided parameters.

find_role(role)
    Returns a role matching the provided name.

find_user(**kwargs)
    Returns a user matching the provided parameters.

get_user(identifier)
    Returns a user matching the specified ID or email address.

remove_role_from_user(user, role)
    Removes a role from a user.

Parameters

•   user -- The user to manipulate. Can be an User object or email

•   role -- The role to remove from the user. Can be a Role object or string role name

set_uniquifier(user, uniquifier=None)
    Set user's authentication token uniquifier. This will immediately render outstanding auth tokens invalid.

Parameters

•   user -- User to modify

•   uniquifier -- Unique value - if none then uuid.uuid4().hex is used

    This method is a no-op if the user model doesn't contain the attribute fs_uniquifier

    New in version 3.3.0.

toggle_active(user)
    Toggles a user's active status. Always returns True.

class flask_security.SQLAlchemySessionUserDatastore(session, user_model, role_model)
    A SQLAlchemy datastore implementation for Flask-Security that assumes the use of the flask_sqlalchemy_session extension.

activate_user(user)
    Activates a specified user. Returns True if a change was made.

Parameters
    user -- The user to activate

add_role_to_user(user, role)
    Adds a role to a user.

Parameters

•   user -- The user to manipulate. Can be an User object or email

•   role -- The role to add to the user. Can be a Role object or string role name

create_role(**kwargs)
    Creates and returns a new role from the given parameters. Supported params (depending on RoleModel):

Parameters

•   name -- Role name

•   permissions --

    a comma delimited list of permissions, a set or a list. These are user-defined strings that correspond to strings used with @permissions_required()

    New in version 3.3.0.

create_user(**kwargs)
    Creates and returns a new user from the given parameters.

Parameters

•   email -- required.

•   password -- Hashed password.

•   roles -- list of roles to be added to user. Can be Role objects or strings

    DANGER: Be aware that whatever password is passed in will be stored directly in the DB. Do NOT pass in a plaintext password! Best practice is to pass in hash_password(plaintext_password).

    The new user's active property will be set to true.

deactivate_user(user)
    Deactivates a specified user. Returns True if a change was made.

    This will immediately disallow access to all endpoints that require authentication either via session or tokens. The user will not be able to log in again.

Parameters
    user -- The user to deactivate

delete_user(user)
    Deletes the specified user.

Parameters
    user -- The user to delete

find_or_create_role(name, **kwargs)
    Returns a role matching the given name or creates it with any additionally provided parameters.

find_role(role)
    Returns a role matching the provided name.

find_user(**kwargs)
    Returns a user matching the provided parameters.

get_user(identifier)
    Returns a user matching the specified ID or email address.

remove_role_from_user(user, role)
    Removes a role from a user.

Parameters

•   user -- The user to manipulate. Can be an User object or email

•   role -- The role to remove from the user. Can be a Role object or string role name

set_uniquifier(user, uniquifier=None)
    Set user's authentication token uniquifier. This will immediately render outstanding auth tokens invalid.

Parameters

•   user -- User to modify

•   uniquifier -- Unique value - if none then uuid.uuid4().hex is used

    This method is a no-op if the user model doesn't contain the attribute fs_uniquifier

    New in version 3.3.0.

toggle_active(user)
    Toggles a user's active status. Always returns True.

class flask_security.MongoEngineUserDatastore(db, user_model, role_model)
    A MongoEngine datastore implementation for Flask-Security that assumes the use of the Flask-MongoEngine extension.

activate_user(user)
    Activates a specified user. Returns True if a change was made.

Parameters
    user -- The user to activate

add_role_to_user(user, role)
    Adds a role to a user.

Parameters

•   user -- The user to manipulate. Can be an User object or email

•   role -- The role to add to the user. Can be a Role object or string role name

create_role(**kwargs)
    Creates and returns a new role from the given parameters. Supported params (depending on RoleModel):

Parameters

•   name -- Role name

•   permissions --

    a comma delimited list of permissions, a set or a list. These are user-defined strings that correspond to strings used with @permissions_required()

    New in version 3.3.0.

create_user(**kwargs)
    Creates and returns a new user from the given parameters.

Parameters

•   email -- required.

•   password -- Hashed password.

•   roles -- list of roles to be added to user. Can be Role objects or strings

    DANGER: Be aware that whatever password is passed in will be stored directly in the DB. Do NOT pass in a plaintext password! Best practice is to pass in hash_password(plaintext_password).

    The new user's active property will be set to true.

deactivate_user(user)
    Deactivates a specified user. Returns True if a change was made.

    This will immediately disallow access to all endpoints that require authentication either via session or tokens. The user will not be able to log in again.

Parameters
    user -- The user to deactivate

delete_user(user)
    Deletes the specified user.

Parameters
    user -- The user to delete

find_or_create_role(name, **kwargs)
    Returns a role matching the given name or creates it with any additionally provided parameters.

find_role(role)
    Returns a role matching the provided name.

find_user(**kwargs)
    Returns a user matching the provided parameters.

get_user(identifier)
    Returns a user matching the specified ID or email address.

remove_role_from_user(user, role)
    Removes a role from a user.

Parameters

•   user -- The user to manipulate. Can be an User object or email

•   role -- The role to remove from the user. Can be a Role object or string role name

set_uniquifier(user, uniquifier=None)
    Set user's authentication token uniquifier. This will immediately render outstanding auth tokens invalid.

Parameters

•   user -- User to modify

•   uniquifier -- Unique value - if none then uuid.uuid4().hex is used

    This method is a no-op if the user model doesn't contain the attribute fs_uniquifier

    New in version 3.3.0.

toggle_active(user)
    Toggles a user's active status. Always returns True.

class flask_security.PeeweeUserDatastore(db, user_model, role_model, role_link)
    A PeeweeD datastore implementation for Flask-Security that assumes the use of Peewee Flask utils.

Parameters

•   user_model -- A user model class definition

•   role_model -- A role model class definition

•   role_link -- A model implementing the many-to-many user-role relation

activate_user(user)
    Activates a specified user. Returns True if a change was made.

Parameters
    user -- The user to activate

add_role_to_user(user, role)
    Adds a role to a user.

Parameters

•   user -- The user to manipulate

•   role -- The role to add to the user

create_role(**kwargs)
    Creates and returns a new role from the given parameters. Supported params (depending on RoleModel):

Parameters

•   name -- Role name

•   permissions --

    a comma delimited list of permissions, a set or a list. These are user-defined strings that correspond to strings used with @permissions_required()

    New in version 3.3.0.

create_user(**kwargs)
    Creates and returns a new user from the given parameters.

deactivate_user(user)
    Deactivates a specified user. Returns True if a change was made.

    This will immediately disallow access to all endpoints that require authentication either via session or tokens. The user will not be able to log in again.

Parameters
    user -- The user to deactivate

delete_user(user)
    Deletes the specified user.

Parameters
    user -- The user to delete

find_or_create_role(name, **kwargs)
    Returns a role matching the given name or creates it with any additionally provided parameters.

find_role(role)
    Returns a role matching the provided name.

find_user(**kwargs)
    Returns a user matching the provided parameters.

get_user(identifier)
    Returns a user matching the specified ID or email address.

remove_role_from_user(user, role)
    Removes a role from a user.

Parameters

•   user -- The user to manipulate

•   role -- The role to remove from the user

set_uniquifier(user, uniquifier=None)
    Set user's authentication token uniquifier. This will immediately render outstanding auth tokens invalid.

Parameters

•   user -- User to modify

•   uniquifier -- Unique value - if none then uuid.uuid4().hex is used

    This method is a no-op if the user model doesn't contain the attribute fs_uniquifier

    New in version 3.3.0.

toggle_active(user)
    Toggles a user's active status. Always returns True.

class flask_security.PonyUserDatastore(db, user_model, role_model)
    A Pony ORM datastore implementation for Flask-Security.

    Code primarily from https://github.com/ET-CS but taken over after being abandoned.

activate_user(user)
    Activates a specified user. Returns True if a change was made.

Parameters
    user -- The user to activate

add_role_to_user(*args, **kwargs)
    Adds a role to a user.

Parameters

•   user -- The user to manipulate. Can be an User object or email

•   role -- The role to add to the user. Can be a Role object or string role name

create_role(**kwargs)
    Creates and returns a new role from the given parameters. Supported params (depending on RoleModel):

Parameters

•   name -- Role name

•   permissions --

    a comma delimited list of permissions, a set or a list. These are user-defined strings that correspond to strings used with @permissions_required()

    New in version 3.3.0.

create_user(**kwargs)
    Creates and returns a new user from the given parameters.

Parameters

•   email -- required.

•   password -- Hashed password.

•   roles -- list of roles to be added to user. Can be Role objects or strings

    DANGER: Be aware that whatever password is passed in will be stored directly in the DB. Do NOT pass in a plaintext password! Best practice is to pass in hash_password(plaintext_password).

    The new user's active property will be set to true.

deactivate_user(user)
    Deactivates a specified user. Returns True if a change was made.

    This will immediately disallow access to all endpoints that require authentication either via session or tokens. The user will not be able to log in again.

Parameters
    user -- The user to deactivate

delete_user(user)
    Deletes the specified user.

Parameters
    user -- The user to delete

find_or_create_role(name, **kwargs)
    Returns a role matching the given name or creates it with any additionally provided parameters.

find_role(role)
    Returns a role matching the provided name.

find_user(**kwargs)
    Returns a user matching the provided parameters.

get_user(identifier)
    Returns a user matching the specified ID or email address.

remove_role_from_user(user, role)
    Removes a role from a user.

Parameters

•   user -- The user to manipulate. Can be an User object or email

•   role -- The role to remove from the user. Can be a Role object or string role name

set_uniquifier(user, uniquifier=None)
    Set user's authentication token uniquifier. This will immediately render outstanding auth tokens invalid.

Parameters

•   user -- User to modify

•   uniquifier -- Unique value - if none then uuid.uuid4().hex is used

    This method is a no-op if the user model doesn't contain the attribute fs_uniquifier

    New in version 3.3.0.

toggle_active(user)
    Toggles a user's active status. Always returns True.

  Utils

flask_security.login_user(user, remember=None)
    Perform the login routine.

    If SECURITY_TRACKABLE is used, make sure you commit changes after this request (i.e. app.security.datastore.commit()).

Parameters

•   user -- The user to login

•   remember -- Flag specifying if the remember cookie should be set. Defaults to False

flask_security.logout_user()
    Logs out the current user.

    This will also clean up the remember me cookie if it exists.

    This sends an identity_changed signal to note that the current identity is now the AnonymousIdentity

flask_security.get_hmac(password)
    Returns a Base64 encoded HMAC+SHA512 of the password signed with the salt specified by SECURITY_PASSWORD_SALT.

Parameters
    password -- The password to sign

flask_security.verify_password(password, password_hash)
    Returns True if the password matches the supplied hash.

Parameters

•   password -- A plaintext password to verify

•   password_hash -- The expected hash value of the password (usually from your database)

flask_security.verify_and_update_password(password, user)
    Returns True if the password is valid for the specified user.

    Additionally, the hashed password in the database is updated if the hashing algorithm happens to have changed.

    N.B. you MUST call DB commit if you are using a session-based datastore (such as SqlAlchemy) since the user instance might have been altered (i.e. app.security.datastore.commit()). This is usually handled in the view.

Parameters

•   password -- A plaintext password to verify

•   user -- The user to verify against

flask_security.hash_password(password)
    Hash the specified plaintext password.

    Unless the hash algorithm (as specified by SECURITY_PASSWORD_HASH) is listed in the configuration variable SECURITY_PASSWORD_SINGLE_HASH, perform a double hash - first create an HMAC from the plaintext password and the value of SECURITY_PASSWORD_SALT, then use the configured hashing algorithm. This satisfies OWASP/ASVS section 2.4.5: 'provide additional iteration of a key derivation'.

    New in version 2.0.2.

Parameters
    password -- The plaintext password to hash

flask_security.url_for_security(endpoint, **values)
    Return a URL for the security blueprint

Parameters

•   endpoint -- the endpoint of the URL (name of the function)

•   values -- the variable arguments of the URL rule

•   _external -- if set to True, an absolute URL is generated. Server address can be changed via SERVER_NAME configuration variable which defaults to localhost.

•   _anchor -- if provided this is added as anchor to the URL.

•   _method -- if provided this explicitly specifies an HTTP method.

flask_security.send_mail(subject, recipient, template, **context)
    Send an email via the Flask-Mail extension.

Parameters

•   subject -- Email subject

•   recipient -- Email recipient

•   template -- The name of the email template

•   context -- The context to render the template with

flask_security.get_token_status(token, serializer, max_age=None, return_data=False)
    Get the status of a token.

Parameters

•   token -- The token to check

•   serializer -- The name of the seriailzer. Can be one of the following: confirm, login, reset

•   max_age -- The name of the max age config option. Can be on of the following: CONFIRM_EMAIL, LOGIN, RESET_PASSWORD

flask_security.get_url(endpoint_or_url, qparams=None)
    Returns a URL if a valid endpoint is found. Otherwise, returns the provided value.

Parameters

•   endpoint_or_url -- The endpoint name or URL to default to

•   qparams -- additional query params to add to end of url

Returns
    URL

flask_security.transform_url(url, qparams=None, **kwargs)
    Modify url

Parameters

•   url -- url to transform (can be relative)

•   qparams -- additional query params to add to end of url

•   kwargs -- pieces of URL to modify - e.g. netloc=localhost:8000

Returns
    Modified URL

    New in version 3.2.0.

class flask_security.FsJsonEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)
    Flask-Security JSON encoder. Extends Flask's JSONencoder to handle lazy-text.

    New in version 3.3.0.

class flask_security.SmsSenderBaseClass

abstract send_sms(from_number, to_number, msg)
    Abstract method for sending sms messages

    New in version 3.2.0.

class flask_security.SmsSenderFactory

classmethod createSender(name, *args, **kwargs)
    Initialize an SMS sender.

Parameters
    name -- Name as registered in SmsSenderFactory:senders (e.g. 'Twilio')

    New in version 3.2.0.

  Signals

See the Flask documentation on signals for information on how to use these signals in your code.

See the documentation for the signals provided by the Flask-Login and Flask-Principal extensions. In addition to those signals, Flask-Security sends the following signals.

user_registered
    Sent when a user registers on the site. In addition to the app (which is the sender), it is passed user and confirm_token arguments.

user_confirmed
    Sent when a user is confirmed. In addition to the app (which is the sender), it is passed a user argument.

confirm_instructions_sent
    Sent when a user requests confirmation instructions. In addition to the app (which is the sender), it is passed a user argument.

login_instructions_sent
    Sent when passwordless login is used and user logs in. In addition to the app (which is the sender), it is passed user and login_token arguments.

password_reset
    Sent when a user completes a password reset. In addition to the app (which is the sender), it is passed a user argument.

password_changed
    Sent when a user completes a password change. In addition to the app (which is the sender), it is passed a user argument.

reset_password_instructions_sent
    Sent when a user requests a password reset. In addition to the app (which is the sender), it is passed user and token arguments.

tf_code_confirmed
    Sent when a user performs two-factor authentication login on the site. In addition to the app (which is the sender), it is passed user and method arguments.

tf_profile_changed
    Sent when two-factor is used and user logs in. In addition to the app (which is the sender), it is passed user and method arguments.

tf_disabled
    Sent when two-factor is disabled. In addition to the app (which is the sender), it is passed user argument.

tf_security_token_sent
    Sent when a two factor security/access code is sent. In addition to the app (which is the sender), it is passed user, method, and token arguments.

ADDITIONAL NOTES

  Contributing

Contributions are welcome. If you would like add features or fix bugs, please review the information below.

One source of history or ideas are the bug reports. There you can find ideas for requested features, or the remains of rejected ideas.

If you have a 'big idea' - please file an issue first so it can be discussed prior to you spending a lot of time developing. New features need to be generally useful - if your feature has limited applicability, consider making a small change that ENABLES your feature, rather than trying to get the entire feature into Flask-Security.

  Checklist

•   All new code and bug fixes need unit tests

•   If you change/add to the external API be sure to update docs/openapi.yaml

•   Additions to configuration variables and/or messages must be documented

•   Make sure any new public API methods have good docstrings, are picked up by the api.rst document, and are exposed in __init__.py if appropriate.

•   Add appropriate info to CHANGES.rst

  Getting the code

The code is hosted on a GitHub repo at https://github.com/jwag956/flask-security. To get a working environment, follow these steps:

1.  (Optional, but recommended) Create a Python 3.6 (or greater) virtualenv to work in, and activate it.

2.  Fork the repo Flask-Security (look for the "Fork" button).

3.  Clone your fork locally:

    $ git clone https://github.com/<your-username>/flask-security

4.  Create a branch for local development:

    $ git checkout -b name-of-your-bugfix-or-feature

5.  Change directory to flask_security:

    $ cd flask_security

6.  Install the requirements:

    $ pip install -e .[tests]

7.  Develop the Feature/Bug Fix and edit

8.  Write Tests for your code in:

    tests/

9.  When done, verify unit tests, syntax etc. all pass:

    $ python setup.py test
    $ python setup.py build_sphinx compile_catalog

10. When the tests are successful, commit your changes and push your branch to GitHub:

    $ git add .
    $ git commit -m "Your detailed description of your changes."
    $ git push origin name-of-your-bugfix-or-feature

11. Submit a pull request through the GitHub website.

12. Be sure that the CI tests and coverage checks pass.

  Updating the Swagger API document

When making changes to the external API, you need to update the openapi.yaml formal specification. To do this - install the swagger editor locally:

$ npm -g install swagger-editor-dist http-server

Then in a browser navigate to:

file:///usr/local/lib/node_modules/swagger-editor-dist/index.html#

Edit - it is a WYSIWYG editor and will show you errors. Once you save (as yaml) you need to look at what it will render as:

$ python setup.py build_sphinx
$ http-server

Then in your browser navigate to:

http://localhost:8081/docs/_build/html/index.html
or
http://localhost:8081/docs/_build/html/_static/openapi_view.html

Please note that changing openapi.yaml won't re-trigger a docs build - so you might have to manually delete docs/_build.

  Testing

Unit tests are critical since Flask-Security is a piece of middleware. They also help other contributors understand any subtleties in the code and edge conditions that need to be handled.

  Datastore

By default the unit tests use an in-memory sqlite DB to test datastores (except for MongoDatastore which uses mongomock). While this is sufficient for most changes, changes to the datastore layer require testing against a real DB (the CI tests test against postgres). It is easy to run the unit tests against a real DB instance. First of course install the DB locally then:

# For postgres
python setup.py test --realdburl postgres://<user>@localhost/
# For mysql
python setup.py test --realdburl "mysql+pymysql://root:<password>@localhost/"

  Views

Much of Flask-Security is concerned with form-based views. These can be difficult to test especially translations etc. In the tests directory is a stand-alone Flask application view_scaffold.py that can be run and you can point your browser to it and walk through the various views.

  Flask-Security Changelog

Here you can see the full list of changes between each Flask-Security release.

  Version 3.4.0

Released TBD

  Version 3.3.3

Released February 11, 2020

Minor changes required to work with latest released Werkzeug and Flask-Login.

  Version 3.3.2

Released December 7, 2019

•   (#215) Fixed 2FA totp secret regeneration bug (kishi85)

•   (#172) Fixed 'next' redirect error in login view

•   (#221) Fixed regressions in login view when already authenticated user again does a GET or POST.

•   (#219) Added example code for unit testing FS protected routes.

•   (#223) Integrated two-factor auth into registration and confirmation.

    Thanks to kuba-lilz and kishi85 for finding and providing detailed issue reports.

    In Flask-Security 3.3.0 the login view was changed to allow already authenticated users to access the view. Prior to 3.3.0, the login view was protected with @anonymous_user_required - so any access (via GET or POST) would simply redirect the user to the POST_LOGIN_VIEW. With the 3.3.0 changes, both GET and POST behaved oddly. GET simply returned the login template, and POST attempted to log out the current user, and log in the new user. This was problematic since this couldn't possibly work with CSRF. The old behavior has been restored, with the subtle change that older Flask-Security releases did not look at "next" in the form or request for the redirect, and now, all redirects from the login view will honor "next".

  Version 3.3.1

Released November 16, 2019

•   (#197) Add Quart compatibility (Ristellise)

•   (#194) Add Python 3.8 support into CI (jdevera)

•   (#196) Improve docs around Single Page Applications and React (acidjunk)

•   (#201) fsqla model was added to __init__.py making Sqlalchemy a required package. That is wrong and has been removed. Applications must now explicitly import from flask_security.models

•   (#204) Fix/improve examples and quickstart to show one MUST call hash_password() when creating users programmatically. Also show real SECRET_KEYs and PASSWORD_SALTs and how to generate them.

•   (#209) Add argon2 as an allowable password hash.

•   (#210) Improve integration with Flask-Admin. Actually - this PR improves localization support by adding a method _fsdomain to jinja2's global environment. Added documentation around localization.

  Version 3.3.0

Released September 26, 2019

There are several default behavior changes that might break existing applications. Most have configuration variables that restore prior behavior.

If you use Authentication Tokens (rather than session cookies) you MUST make a (small) change. Please see below for details.

•   (#120) Native support for Permissions as part of Roles. Endpoints can be protected via permissions that are evaluated based on role(s) that the user has.

•   (#126, #93, #96) Revamp entire CSRF handling. This adds support for Single Page Applications and having CSRF protection for browser(session) authentication but ignored for token based authentication. Add extensive documentation about all the options.

•   (#156) Token authentication is slow. Please see below for details on how to enable a new, fast implementation.

•   (#130) Enable applications to provide their own render_json() method so that they can create unified API responses.

•   (#121) Unauthorization callback not quite right. Split into 2 different callbacks - one for unauthorized and one for unauthenticated. Made default unauthenticated handler use Flask-Login's unauthenticated method to make everything uniform. Extensive documentation added. Security.unauthorized_callback() has been deprecated.

•   (#120) Add complete User and Role model mixins that support all features. Modify tests and Quickstart documentation to show how to use these. Please see responsetopic for details.

•   Improve documentation for UserDatastore.create_user() to make clear that hashed password should be passed in.

•   Improve documentation for UserDatastore and verify_and_update_password() to make clear that caller must commit changes to DB if using a session based datastore.

•   (#122) Clarify when to use confirm_register_form rather than register_form.

•   Fix bug in 2FA that didn't commit DB after using verify_and_update_password.

•   Fix bug(s) in UserDatastore where changes to user active flag weren't being added to DB.

•   (#127) JSON response was failing due to LazyStrings in error response.

•   (#117) Making a user inactive should stop all access immediately.

•   (#134) Confirmation token can no longer be reused. Added SECURITY_AUTO_LOGIN_AFTER_CONFIRM option for applications that don't want the user to be automatically logged in after confirmation (defaults to True - existing behavior).

•   (#159) The /register endpoint returned the Authentication Token even though confirmation was required. This was a huge security hole - it has been fixed.

•   (#160) The 2FA totp_secret would be regenerated upon submission, making QRCode not work. (malware-watch)

•   (#166) default_render_json() uses flask.make_response and forces the Content-Type to JSON for generating the response (koekie)

•   (#166) SECURITY_MSG_UNAUTHENTICATED added to the configuration.

•   (#168) When using the @auth_required or @auth_token_required decorators, the token would be verified twice, and the DB would be queried twice for the user. Given how slow token verification is - this was a significant issue. That has been fixed.

•   (#84) The anonymous_user_required() was not JSON friendly - always performing a redirect. Now, if the request 'wants' a JSON response - it will receive a 400 with an error message defined by SECURITY_MSG_ANONYMOUS_USER_REQUIRED.

•   (#145) Improve 2FA templates to that they can be localized. (taavie)

•   (#173) SECURITY_UNAUTHORIZED_VIEW didn't accept a url (just an endpoint). All other view configurations did. That has been fixed.

  Possible compatibility issues

•   (#164) In prior releases, the Authentication Token was returned as part of the JSON response to each successful call to /login, /change, or /reset/{token} API call. This is not a great idea since for browser-based UIs that used JSON request/response, and used session based authentication - they would be sent this token - even though it was likely ignored. Since these tokens by default have no expiration time this exposed a needless security hole. The new default behavior is to ONLY return the Authentication Token from those APIs if the query param include_auth_token is added to the request. Prior behavior can be restored by setting the SECURITY_BACKWARDS_COMPAT_AUTH_TOKEN configuration variable.

•   (#120) RoleMixin now has a method get_permissions() which is called as part each request to add Permissions to the authenticated user. It checks if the RoleModel has a property permissions and assumes it is a comma separated string of permissions. If your model already has such a property this will likely fail. You need to override get_permissions() and simply return an emtpy set.

•   (#121) Changes the default (failure) behavior for views protected with @auth_required, @token_auth_required, or @http_auth_required. Before, a 401 was returned with some stock html. Now, Flask-Login.unauthorized() is called (the same as @login_required does) - which by default redirects to a login page/view. If you had provided your own Security.unauthorized_callback() there are no changes - that will still be called first. The old default behavior can be restored by setting SECURITY_BACKWARDS_COMPAT_UNAUTHN to True. Please see responsetopic for details.

•   (#127) Fix for LazyStrings in json error response. The fix for this has Flask-Security registering its own JsonEncoder on its blueprint. If you registered your own JsonEncoder for your app - it will no longer be called when serializing responses to Flask-Security endpoints. You can register your JsonEncoder on Flask-Security's blueprint by sending it as json_encoder_cls as part of initialization. Be aware that your JsonEncoder needs to handle LazyStrings (see speaklater).

•   (#84) Prior to this fix - anytime the decorator anonymous_user_required() failed, it caused a redirect to the post_login_view. Now, if the caller wanted a JSON response, it will return a 400.

•   (#156) Faster Authentication Token introduced the following non-backwards compatible behavior change:

•   Since the old Authentication Token algorithm used the (hashed) user's password, those tokens would be invalidated whenever the user changed their password. This is not likely to be what most users expect. Since the new Authentication Token algorithm doesn't refer to the user's password, changing the user's password won't invalidate outstanding Authentication Tokens. The method UserDatastore.set_uniquifier() can be used by an administrator to change a user's fs_uniquifier - but nothing the user themselves can do to invalidate their Authentication Tokens. Setting the SECURITY_BACKWARDS_COMPAT_AUTH_TOKEN_INVALIDATE configuration variable will cause the user's fs_uniquifier to be changed when they change their password, thus restoring prior behavior.

  New fast authentication token implementation

Current auth tokens are slow because they use the user's password (hashed) as a uniquifier (the user id isn't really enough since it might be reused). This requires checking the (hashed) password against what is in the token on EVERY request - however hashing is (on purpose) slow. So this can add almost a whole second to every request.

To solve this, a new attribute in the User model was added - fs_uniquifier. If this is present in your User model, then it will be used instead of the password for ensuring the token corresponds to the correct user. This is very fast. If that attribute is NOT present - then the behavior falls back to the existing (slow) method.

  DB Migration

To use the new UserModel mixins or to add the column user.fs_uniquifier to speed up token authentication, a schema AND data migration needs to happen. If you are using Alembic the schema migration is easy - but you need to add fs_uniquifier values to all your existing data. You can add code like this to your migrations::update method:

# be sure to MODIFY this line to make nullable=True:
op.add_column('user', sa.Column('fs_uniquifier', sa.String(length=64), nullable=True))
# update existing rows with unique fs_uniquifier
import uuid
user_table = sa.Table('user', sa.MetaData(), sa.Column('id', sa.Integer, primary_key=True),
                      sa.Column('fs_uniquifier', sa.String))
conn = op.get_bind()
for row in conn.execute(sa.select([user_table.c.id])):
    conn.execute(user_table.update().values(fs_uniquifier=uuid.uuid4().hex).where(user_table.c.id == row['id']))
# finally - set nullable to false
op.alter_column('user', 'fs_uniquifier', nullable=False)

  Version 3.2.0

Released June 26th 2019

•   (opr #839) Support caching of authentication token (eregnier). This adds a new configuration variable SECURITY_USE_VERIFY_PASSWORD_CACHE which enables a cache (with configurable TTL) for authentication tokens. This is a big performance boost for those accessing Flask-Security via token as opposed to session.

•   (#81) Support for JSON/Single-Page-Application. This completes support for non-form based access to Flask-Security. See PR for details. (jwag956)

•   (#79 Add POST logout to enhance JSON usage (jwag956).

•   (#73) Fix get_user for various DBs (jwag956). This is a more complete fix than in opr #633.

•   (#78, #103) Add formal openapi API spec (jwag956).

•   (#86, #94, #98, #101, #104) Add Two-factor authentication (opr #842) (baurt, jwag956).

•   (#108) Fix form field label translations (jwag956)

•   (#115) Fix form error message translations (upstream #801) (jwag956)

•   (#87) Convert entire repo to Black (baurt)

  Version 3.1.0

Released never

•   (opr #487) Use Security.render_template in mails too (noirbizarre)

•   (opr #679) Optimize DB accesses by using an SQL JOIN when retrieving a user. (nfvs)

•   (opr #697) Add base template to security templates (grihabor)

•   (opr #633) datastore: get user by numeric identity attribute (jirikuncar)

•   (opr #703) bugfix: support application factory pattern (briancappello)

•   (opr #714) Make SECURITY_PASSWORD_SINGLE_HASH a list of scheme ignoring double hash (noirbizarre )

•   (opr #717) Allow custom login_manager to be passed in to Flask-Security (jaza)

•   (opr #727) Docs for OAauth2-based custom login manager (jaza)

•   (opr #779) core: make the User model check the password (mklassen)

•   (opr #730) Customizable send_mail (abulte)

•   (opr #726) core: fix default for UNAUTHORIZED_VIEW (jirijunkar)

    These should all be backwards compatible.

    Possible compatibility issues:

•   #487 - prior to this, render_template() was overiddable for views, but not emails. If anyone actually relied on this behavior, this has changed.

•   #703 - get factory pattern working again. There was a very complex dance between Security() instantiation and init_app regarding kwargs. This has been rationalized (hopefully).

•   #679 - SqlAlchemy SQL improvement. It is possible you will get the following error:

    Got exception during processing: <class 'sqlalchemy.exc.InvalidRequestError'> -
    'User.roles' does not support object population - eager loading cannot be applied.

    This is likely solveable by removing lazy='dynamic' from your Role definition.

    Performance improvements:

•   #679 - for sqlalchemy, for each request, there would be 2 DB accesses - now there is one.

    Testing: For datastores operations, Sqlalchemy, peewee, pony were all tested against sqlite, postgres, and mysql real databases.

  Version 3.0.2

Released April 30th 2019

•   (opr #439) HTTP Auth respects SECURITY_USER_IDENTITY_ATTRIBUTES (pnpnpn)

•   (opr #660) csrf_enabled` deprecation fix (abulte)

•   (opr #671) Fix referrer loop in _get_unauthorized_view(). (nfvs)

•   (opr #675) Fix AttributeError in _request_loader (sbagan)

•   (opr #676) Fix timing attack on login form (cript0nauta)

•   (opr #683) Close db connection after running tests (reambus)

•   (opr #691) docs: add password salt to SQLAlchemy app example (KshitijKarthick)

•   (opr #692) utils: fix incorrect email sender type (switowski)

•   (opr #696) Fixed broken Click link (williamhatcher)

•   (opr #722) Fix password recovery confirmation on deleted user (kesara)

•   (opr #747) Update login_user.html (rickwest)

•   (opr #748) i18n: configurable the dirname domain (escudero)

•   (opr #835) adds relevant user to reset password form for validation purposes (fuhrysteve)

    These are bug fixes and a couple very small additions. No change in behavior and no new functionality. 'opr#' is the original pull request from https://github.com/mattupstate/flask-security

  Version 3.0.1

Released April 28th 2019

•   Support 3.7 as part of CI

•   Rebrand to this forked repo

•   (#15) Build docs and translations as part of CI

•   (#17) Move to msgcheck from pytest-translations

•   (opr #669) Fix for Read the Docs (jirikuncar)

•   (opr #710) Spanish translation (maukoquiroga)

•   (opr #712) i18n: improvements of German translations (eseifert)

•   (opr #713) i18n: add Portuguese (Brazilian) translation (dinorox)

•   (opr #719) docs: fix anchor links and typos (kesara)

•   (opr #751) i18n: fix missing space (abulte)

•   (opr #762) docs: fixed proxy import (lsmith)

•   (opr #767) Update customizing.rst (allanice001)

•   (opr #776) i18n: add Portuguese (Portugal) translation (micael-grilo)

•   (opr #791) Fix documentation for mattupstate#781 (fmerges)

•   (opr #796) Chinese translations (Steinkuo)

•   (opr #808) Clarify that a commit is needed after login_user (christophertull)

•   (opr #823) Add Turkish translation (Admicos)

•   (opr #831) Catalan translation (miceno)

    These are all documentation and i18n changes - NO code changes. All except the last 3 were accepted and reviewed by the original Flask-Security team. Thanks as always to all the contributors.

  Version 3.0.0

Released May 29th 2017

•   Fixed a bug when user clicking confirmation link after confirmation and expiration causes confirmation email to resend. (see #556)

•   Added support for I18N.

•   Added options SECURITY_EMAIL_PLAINTEXT and SECURITY_EMAIL_HTML for sending respecively plaintext and HTML version of email.

•   Fixed validation when missing login information.

•   Fixed condition for token extraction from JSON body.

•   Better support for universal bdist wheel.

•   Added port of CLI using Click configurable using options SECURITY_CLI_USERS_NAME and SECURITY_CLI_ROLES_NAME.

•   Added new configuration option SECURITY_DATETIME_FACTORY which can be used to force default timezone for newly created datetimes. (see mattupstate/flask-security#466)

•   Better IP tracking if using Flask 0.12.

•   Renamed deprecated Flask-WFT base form class.

•   Added tests for custom forms configured using app config.

•   Added validation and tests for next argument in logout endpoint. (see #499)

•   Bumped minimal required versions of several packages.

•   Extended test matric on Travis CI for minimal and released package versions.

•   Added of .editorconfig and forced tests for code style.

•   Fixed a security bug when validating a confirmation token, also checks if the email that the token was created with matches the user's current email.

•   Replaced token loader with request loader.

•   Changed trackable behavior of login_user when IP can not be detected from a request from 'untrackable' to None value.

•   Use ProxyFix instead of inspecting X-Forwarded-For header.

•   Fix identical problem with app as with datastore.

•   Removed always-failing assertion.

•   Fixed failure of init_app to set self.datastore.

•   Changed to new style flask imports.

•   Added proper error code when returning JSON response.

•   Changed obsolette Required validator from WTForms to DataRequired. Bumped Flask-WTF to 0.13.

•   Fixed missing SECURITY_SUBDOMAIN in config docs.

•   Added cascade delete in PeeweeDatastore.

•   Added notes to docs about SECURITY_USER_IDENTITY_ATTRIBUTES.

•   Inspect value of SECURITY_UNAUTHORIZED_VIEW.

•   Send password reset instructions if an attempt has expired.

•   Added "Forgot password?" link to LoginForm description.

•   Upgraded passlib, and removed bcrypt version restriction.

•   Removed a duplicate line ('retype_password': 'Retype Password') in forms.py.

•   Various documentation improvement.

  Version 1.7.5

Released December 2nd 2015

•   Added SECURITY_TOKEN_MAX_AGE configuration setting

•   Fixed calls to SQLAlchemyUserDatastore.get_user(None) (this now returns False instead of raising a TypeError

•   Fixed URL generation adding extra slashes in some cases (see GitHub #343)

•   Fixed handling of trackable IP addresses when the X-Forwarded-For header contains multiple values

•   Include WWW-Authenticate headers in @auth_required authentication checks

•   Fixed error when check_token function is used with a json list

•   Added support for custom AnonymousUser classes

•   Restricted forgot_password endpoint to anonymous users

•   Allowed unauthorized callback to be overridden

•   Fixed issue where passwords cannot be reset if currently set to None

•   Ensured that password reset tokens are invalidated after use

•   Updated is_authenticated and is_active functions to support Flask-Login changes

•   Various documentation improvements

  Version 1.7.4

Released October 13th 2014

•   Fixed a bug related to changing existing passwords from plaintext to hashed

•   Fixed a bug in form validation that did not enforce case insensivitiy

•   Fixed a bug with validating redirects

  Version 1.7.3

Released June 10th 2014

•   Fixed a bug where redirection to SECURITY_POST_LOGIN_VIEW was not respected

•   Fixed string encoding in various places to be friendly to unicode

•   Now using werkzeug.security.safe_str_cmp to check tokens

•   Removed user information from JSON output on /reset responses

•   Added Python 3.4 support

  Version 1.7.2

Released May 6th 2014

•   Updated IP tracking to check for X-Forwarded-For header

•   Fixed a bug regarding the re-hashing of passwords with a new algorithm

•   Fixed a bug regarding the password_changed signal.

  Version 1.7.1

Released January 14th 2014

•   Fixed a bug where passwords would fail to verify when specifying a password hash algorithm

  Version 1.7.0

Released January 10th 2014

•   Python 3.3 support!

•   Dependency updates

•   Fixed a bug when SECURITY_LOGIN_WITHOUT_CONFIRMATION = True did not allow users to log in

•   Added SECURITY_SEND_PASSWORD_RESET_NOTICE_EMAIL configuraiton option to optionally send password reset notice emails

•   Add documentation for @security.send_mail_task

•   Move to request.get_json as request.json is now deprecated in Flask

•   Fixed a bug when using AJAX to change a user's password

•   Added documentation for select functions in the flask_security.utils module

•   Fixed a bug in flask_security.forms.NextFormMixin

•   Added CHANGE_PASSWORD_TEMPLATE configuration option to optionally specify a different change password template

•   Added the ability to specify addtional fields on the user model to be used for identifying the user via the USER_IDENTITY_ATTRIBUTES configuration option

•   An error is now shown if a user tries to change their password and the password is the same as before. The message can be customed with the SECURITY_MSG_PASSWORD_IS_SAME configuration option

•   Fixed a bug in MongoEngineUserDatastore where user model would not be updated when using the add_role_to_user method

•   Added SECURITY_SEND_PASSWORD_CHANGE_EMAIL configuration option to optionally disable password change email from being sent

•   Fixed a bug in the find_or_create_role method of the PeeWee datastore

•   Removed pypy tests

•   Fixed some tests

•   Include CHANGES and LICENSE in MANIFEST.in

•   A bit of documentation cleanup

•   A bit of code cleanup including removal of unnecessary utcnow call and simplification of get_max_age method

  Version 1.6.9

Released August 20th 2013

•   Fix bug in SQLAlchemy datastore's get_user function

•   Fix bug in PeeWee datastore's remove_role_from_user function

•   Fixed import error caused by new Flask-WTF release

  Version 1.6.8

Released August 1st 2013

•   Fixed bug with case sensitivity of email address during login

•   Code cleanup regarding token_callback

•   Ignore validation errors in find_user function for MongoEngineUserDatastore

  Version 1.6.7

Released July 11th 2013

•   Made password length form error message configurable

•   Fixed email confirmation bug that prevented logged in users from confirming their email

  Version 1.6.6

Released June 28th 2013

•   Fixed dependency versions

  Version 1.6.5

Released June 20th 2013

•   Fixed bug in flask.ext.security.confirmable.generate_confirmation_link

  Version 1.6.4

Released June 18th 2013

•   Added SECURITY_DEFAULT_REMEMBER_ME configuration value to unify behavior between endpoints

•   Fixed Flask-Login dependency problem

•   Added optional next parameter to registration endpoint, similar to that of login

  Version 1.6.3

Released May 8th 2013

•   Fixed bug in regards to imports with latest version of MongoEngine

  Version 1.6.2

Released April 4th 2013

•   Fixed bug with http basic auth

  Version 1.6.1

Released April 3rd 2013

•   Fixed bug with signals

  Version 1.6.0

Released March 13th 2013

•   Added Flask-Pewee support

•   Password hashing is now more flexible and can be changed to a different type at will

•   Flask-Login messages are configurable

•   AJAX requests must now send a CSRF token for security reasons

•   Form messages are now configurable

•   Forms can now be extended with more fields

•   Added change password endpoint

•   Added the user to the request context when successfully authenticated via http basic and token auth

•   The Flask-Security blueprint subdomain is now configurable

•   Redirects to other domains are now not allowed during requests that may redirect

•   Template paths can be configured

•   The welcome/register email can now optionally be sent to the user

•   Passwords can now contain non-latin characters

•   Fixed a bug when confirming an account but the account has been deleted

  Version 1.5.4

Released January 6th 2013

•   Fix bug in forms with csrf_enabled parameter not accounting attempts to login using JSON data

  Version 1.5.3

Released December 23rd 2012

•   Change dependency requirement

  Version 1.5.2

Released December 11th 2012

•   Fix a small bug in flask_security.utils.login_user method

  Version 1.5.1

Released November 26th 2012

•   Fixed bug with next form variable

•   Added better documentation regarding Flask-Mail configuration

•   Added ability to configure email subjects

  Version 1.5.0

Released October 11th 2012

•   Major release. Upgrading from previous versions will require a bit of work to accomodate API changes. See documentation for a list of new features and for help on how to upgrade.

  Version 1.2.3

Released June 12th 2012

•   Fixed a bug in the RoleMixin eq/ne functions

  Version 1.2.2

Released April 27th 2012

•   Fixed bug where roles_required and roles_accepted did not pass the next argument to the login view

  Version 1.2.1

Released March 28th 2012

•   Added optional user model mixin parameter for datastores

•   Added CreateRoleCommand to available Flask-Script commands

  Version 1.2.0

Released March 12th 2012

•   Added configuration option SECURITY_FLASH_MESSAGES which can be set to a boolean value to specify if Flask-Security should flash messages or not.

  Version 1.1.0

Initial release Flask-Security was written by Matt Wright and various contributors.

Flask-Security-Too is an independently maintained repo:

  Development Lead

•   Chris Wagner <jwag956@github.com>

  Maintainer

•   Chris Wagner <jwag956@github.com>

  Patches and Suggestions

Alexander Sukharev Alexey Poryadin Andrew J. Camenga Anthony Plunkett Artem Andreev Catherine Wise Chris Haines Christophe Simonis David Ignacio Eric Butler Eskil Heyn Olsen Iuri de Silvio Jay Goel Jiri Kuncar Joe Esposito Joe Hand Josh Purvis Kostyantyn Leschenko Luca Invernizzi Manuel Ebert Martin Maillard Paweł Krześniak Robert Clark Rodrigue Cloutier Rotem Yaari Srijan Choudhary Tristan Escalada Vadim Kotov Walt Askew John Paraskevopoulos Chris Wagner Eric Regnier Gal Stainfeld Ivan Piskunov Tyler Baur

AUTHOR
Matt Wright & Chris Wagner

COPYRIGHT
2012-2019