FLASK-SECURITY(1)

NAME
flask-security - Flask-Security 4.0.1 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/alpha)

9.  Unified sign in (optional)

10. User registration (optional)

11. Login tracking (optional)

12. JSON/Ajax Support

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

•   Flask-Login

•   Flask-Mail

•   Flask-Principal

•   Flask-WTF

•   itsdangerous

•   passlib

•   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 to associate the value of fs_uniquifier with the user. (This enables easily invalidating all existing sessions for a given user without having to change their user id). Flask-WTF integrates with the session as well to provide out of the box CSRF support. Flask-Security extends that to support configurations that would require 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.

  Password Validation and Complexity

Consult pass_validation_topic.

  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 can be used by retrieving the user auth token from an authentication endpoint (e.g. /login, /us-signin). Perform an HTTP POST with a query param of include_auth_token and the authentication details as JSON data. A successful call will return the authentication token. This token can be used in subsequent requests to protected resources. The auth token should be 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. By default that field is fs_uniquifier. This means that if that field is changed (via UserDatastore.set_uniquifier()) then any existing authentication tokens will no longer be valid. This value is changed whenever a user changes their password. If this is not the desired behavior then you can add an additional attribute to the UserModel: fs_token_uniquifier and that will be used instead, thus isolating password changes from authentication tokens. That attribute can be changed via UserDatastore.set_token_uniquifier(). This attribute should have unique=True. Unlike fs_uniquifier, it can be set to nullable - it will automatically be generated at first use if null.

  Two-factor Authentication

This feature is in Beta - mostly due to it being brand new and little to no production soak time

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 an authenticator app such as Google Authenticator, LastPass Authenticator, or Authy. By default, tokens provided by the authenticator app 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 the authenticator app with the secret is generated using the PyQRCode library. Please read 2fa_theory_of_operation for more details.

  Unified Sign In

This feature is in Beta - mostly due to it being brand new and little to no production soak time

Unified sign in provides a generalized login endpoint that takes an identity and a passcode; where (based on configuration):

•   identity is any of SECURITY_USER_IDENTITY_ATTRIBUTES (e.g. email, username, phone)

•   passcode is a password or a one-time code (delivered via email, SMS, or authenticator app)

    Please see this Wikipedia article about multi-factor authentication.

    Using this feature, it is possible to not require the user to have a stored password at all, and just require the use of a one-time code. The mechanisms for generating and delivering the one-time code are similar to common two-factor mechanisms.

    This one-time code can be configured to be delivered via email, SMS or authenticator app - however be aware that NIST does not recommend email for this purpose (though many web sites do so) due to the fact that a) email may travel through many different servers as part of being delivered - and b) is available from any device.

    Using SMS or an authenticator app means you are providing "something you have" (the mobile device) and either "something you know" (passcode to unlock your device) or "something you are" (biometric quality to unlock your device). This effectively means that using a one-time code to sign in, is in fact already two-factor (if using SMS or authenticator app). Many large authentication providers already offer this - here is Microsoft's version.

    Note that by configuring SECURITY_US_ENABLED_METHODS an application can use this endpoint JUST with identity/password or in fact disallow passwords altogether.

    Unified sign in is integrated with two-factor authentication. Since in general there is no need for a second factor if the initial authentication was with SMS or an authenticator application, the SECURITY_US_MFA_REQUIRED configuration determines which primary authentication mechanisms require a second factor. By default limited to email and password (if two-factor is enabled).

    Be aware that by default, the SECURITY_US_SETUP_URL endpoint is protected with a freshness check (see flask_security.auth_required()) which means it requires a session cookie to function properly. This is true even if using JSON payload or token authentication. If you disable the freshness check then sessions aren't required.

    Current Limited Functionality:

•   Change password does not work if a user registers without a password. However forgot-password will allow the user to set a new password.

•   Registration and Confirmation only work with email - so while you can enable multiple authentication methods, you still have to register with email.

  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 their 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.

As with password change - this will update the the user's fs_uniquifier attribute which will invalidate all existing sessions AND (by default) all authentication tokens.

  User Registration

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. User email is validated and normalized using the email_validator package.

  Password Change

Flask-Security comes packaged with a basic change user password view. Unlike password recovery, this endpoint is used when the user is already authenticated. The result of a successful password change is not only a new password, but a new value for fs_uniquifier. This has the effect is immediately invalidating all existing sessions. The change request itself effectively re-logs in the user so a new session is created. Note that since the user is effectively re-logged in, the same signals are sent as when the user normally authenticates.

NOTE The fs_uniquifier by default, controls both sessions and authenticated tokens. Thus changing the password also invalidates all authentication tokens. This may not be desirable behavior, so if the UserModel contains an attribute fs_token_uniquifier, then that will be used when generating authentication tokens and so won't be affected by password changes.

  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

•   Unified sign in 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

These configuration keys are used globally across all features.

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.

    Default: security.

SECURITY_URL_PREFIX
    Specifies the URL prefix for the Flask-Security blueprint.

    Default: None.

SECURITY_SUBDOMAIN
    Specifies the subdomain for the Flask-Security blueprint. If your authenticated content is on a different subdomain, also enable SECURITY_REDIRECT_ALLOW_SUBDOMAINS.

    Default: None.

SECURITY_FLASH_MESSAGES
    Specifies whether or not to flash messages during security procedures.

    Default: True.

SECURITY_I18N_DOMAIN
    Specifies the name for domain used for translations.

    Default: flask_security.

SECURITY_I18N_DIRNAME
    Specifies the directory containing the MO files used for translations. When using flask-babel this can also be a list of directory names - this enables application to override a subset of messages if desired.

    Default: [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).

    Default: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 setting of SECURITY_PASSWORD_HASH.

    Default: ["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).

    Default: 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.

    Default: 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).

    Default: sha256_crypt.

SECURITY_DEPRECATED_HASHING_SCHEMES
    List of deprecated algorithms used for creating and validating tokens.

    Default: hex_md5.

SECURITY_PASSWORD_HASH_OPTIONS
    Specifies additional options to be passed to the hashing method. This is deprecated as of passlib 1.7.

    Deprecated since version 3.4.0: see: SECURITY_PASSWORD_HASH_PASSLIB_OPTIONS

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}.

    New in version 3.3.1.

SECURITY_PASSWORD_LENGTH_MIN
    Minimum required length for passwords.

    Default: 8

    New in version 3.4.0.

SECURITY_PASSWORD_COMPLEXITY_CHECKER
    Set to complexity checker to use (Only zxcvbn supported).

    Default: None

    New in version 3.4.0.

SECURITY_PASSWORD_CHECK_BREACHED
    If not None new/changed passwords will be checked against the database of breached passwords at https://api.pwnedpasswords.com. If set to strict then if the site can't be reached, validation will fail. If set to best-effort failure to reach the site will continue with the rest of password validation.

    Default: None

    New in version 3.4.0.

SECURITY_PASSWORD_BREACHED_COUNT
    Passwords with counts greater than or equal to this value are considered breached.

    Default: 1 - which might be to burdensome for some applications.

    New in version 3.4.0.

SECURITY_PASSWORD_NORMALIZE_FORM
    Passwords are normalized prior to changing or comparing. This satisfies the NIST requirement: 5.1.1.2 Memorized Secret Verifiers. Normalization is performed using the Python unicodedata.normalize() method.

    Default: "NFKD"

    New in version 4.0.0.

SECURITY_TOKEN_AUTHENTICATION_KEY
    Specifies the query string parameter to read when using token authentication.

    Default: auth_token.

SECURITY_TOKEN_AUTHENTICATION_HEADER
    Specifies the HTTP header to read when using token authentication.

    Default: Authentication-Token.

SECURITY_TOKEN_MAX_AGE
    Specifies the number of seconds before an authentication token expires.

    Default: None, meaning the token never expires.

SECURITY_EMAIL_VALIDATOR_ARGS
    Email address are validated using the email_validator package. Its methods have some configurable options - these can be set here and will be passed in. For example setting this to: {"check_deliverability": False} is useful when unit testing if the emails are fake.

    Default: None, meaning use the defaults from email_validator package.

    New in version 4.0.0.

SECURITY_DEFAULT_HTTP_AUTH_REALM
    Specifies the default authentication realm when using basic HTTP auth.

    Default: Login Required

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.

    Default: None which is existing html-style form redirects.

    New in version 3.3.0.

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.

    Default: None.

    New in version 3.3.0.

SECURITY_REDIRECT_ALLOW_SUBDOMAINS
    If True then subdomains (and the root domain) of the top-level host set by Flask's SERVER_NAME configuration will be allowed as post-login redirect targets. This is beneficial if you wish to place your authentiation on one subdomain and authenticated content on another, for example auth.domain.tld and app.domain.tld.

    Default: False.

    New in version 4.0.0.

SECURITY_CSRF_PROTECT_MECHANISMS
    Authentication mechanisms that require CSRF protection. These are the same mechanisms as are permitted in the @auth_required decorator.

    Default: ("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).

    Default: 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.

    Default: {"key": None} which 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.

    Default: X-XSRF-Token.

SECURITY_CSRF_COOKIE_REFRESH_EACH_REQUEST
    By default, csrf_tokens have an expiration (controlled by 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).

    Default: False.

SECURITY_EMAIL_SENDER
    Specifies the email address to send emails as.

    Default: value set to MAIL_DEFAULT_SENDER if Flask-Mail is used otherwise no-reply@localhost.

SECURITY_USER_IDENTITY_ATTRIBUTES
    Specifies which attributes of the user object can be used for credential validation.

    Defines the order and matching that will be applied when validating login credentials (either via standard login form or the unified sign in form). The identity field in the form will be matched in order using this configuration - the FIRST match will then be used to look up the user in the DB.

    Mapping functions take a single argument - identity from the form and should return None if the identity argument isn't in a format suitable for the attribute. If the identity argument format matches, it should be returned, optionally having had some canonicalization performed. The returned result will be used to look up the identity in the UserDataStore using the column name specified in the key.

    The provided flask_security.uia_phone_mapper() for example performs phone number normalization using the phonenumbers package.

    TIP: If your mapper performs any sort of canonicalization/normalization, make sure you apply the exact same transformation in your form validator when setting the field.

    DANGER: Make sure that any attributes listed here are marked Unique in your UserDataStore model.

    DANGER: Make sure your mapper methods guard against malicious user input. For example, if you allow username as an identity method you could use bleach:

    def uia_username_mapper(identity):
        # we allow pretty much anything - but we bleach it.
        return bleach.clean(identity, strip=True)

    Default:

    [
        {"email": {"mapper": uia_email_mapper, "case_insensitive": True}},
    ]

    If you enable SECURITY_UNIFIED_SIGNIN and set sms as a SECURITY_US_ENABLED_METHODS the following would be necessary:

    [
        {"email": {"mapper": uia_email_mapper, "case_insensitive": True}},
        {"us_phone_number": {"mapper": uia_phone_mapper}},
    ]

    Changed in version 4.0.0: Changed from list to list of dict.

SECURITY_USER_IDENTITY_MAPPINGS
    New in version 3.4.0.

    Deprecated since version 4.0.0: Superseded by SECURITY_USER_IDENTITY_ATTRIBUTES

SECURITY_API_ENABLED_METHODS
    Various endpoints of Flask-Security require the caller to be authenticated. This variable controls which of the methods - token, session, basic will be allowed. The default does NOT include basic since if basic is in the list, and if the user is NOT authenticated, then the standard/required response of 401 with the WWW-Authenticate header is returned. This is rarely what the client wants.

    Default: ["session", "token"].

    New in version 4.0.0.

SECURITY_DEFAULT_REMEMBER_ME
    Specifies the default "remember me" value used when logging in a user.

    Default: False.

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).

    Default: 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.

    Default: False.

  Core - Multi-factor

These are used by the Two-Factor and Unified Signin features.

SECURITY_TOTP_SECRETS
    Secret used to encrypt the totp_password both into DB and into the session cookie. Best practice is to set this to:

    from passlib import totp
    "{1: <result of totp.generate_secret()>}"

    See: Totp for details.

    New in version 3.4.0.

SECURITY_TOTP_ISSUER
    Specifies the name of the service or application that the user is authenticating to. This will be the name displayed by most authenticator apps.

    Default: None.

    New in version 3.4.0.

SECURITY_SMS_SERVICE
    Specifies the name of the sms service provider. Out of the box "Twilio" is supported. For other sms service providers you will need to subclass SmsSenderBaseClass and register it:

    SmsSenderFactory.senders[<service-name>] = <service-class>

    Default: Dummy which does nothing.

    New in version 3.4.0.

SECURITY_SMS_SERVICE_CONFIG
    Specifies a dictionary of basic configurations needed for use of a sms service. For "Twilio" the following keys are required (fill in from your Twilio dashboard):

    Default: {'ACCOUNT_SID': NONE, 'AUTH_TOKEN': NONE, 'PHONE_NUMBER': NONE}

    New in version 3.4.0.

SECURITY_PHONE_REGION_DEFAULT
    Assigns a default 'region' for phone numbers used for two-factor or unified sign in. All other phone numbers will require a region prefix to be accepted.

    Default: US

    New in version 3.4.0.

SECURITY_FRESHNESS
    A timedelta used to protect endpoints that alter sensitive information. This is used to protect the endpoint: SECURITY_US_SETUP_URL, and SECURITY_TWO_FACTOR_SETUP_URL. Setting this to a negative number will disable any freshness checking and the endpoints SECURITY_VERIFY_URL, SECURITY_US_VERIFY_URL and SECURITY_US_VERIFY_SEND_CODE_URL won't be registered. Setting this to 0 results in undefined behavior. Please see flask_security.check_and_update_authn_fresh() for details.

    Default: timedelta(hours=24)

    New in version 3.4.0.

SECURITY_FRESHNESS_GRACE_PERIOD
    A timedelta that provides a grace period when altering sensitive information. This is used to protect the endpoint: SECURITY_US_SETUP_URL, and SECURITY_TWO_FACTOR_SETUP_URL. N.B. To avoid strange behavior, be sure to set the grace period less than the freshness period. Please see flask_security.check_and_update_authn_fresh() for details.

    Default: timedelta(hours=1)

    New in version 3.4.0.

  Core - rarely need changing

SECURITY_DATETIME_FACTORY
    Specifies the default datetime factory.

    Default:datetime.datetime.utcnow.

SECURITY_CONFIRM_SALT
    Specifies the salt value when generating confirmation links/tokens.

    Default: "confirm-salt".

SECURITY_RESET_SALT
    Specifies the salt value when generating password reset links/tokens.

    Default: "reset-salt".

SECURITY_LOGIN_SALT
    Specifies the salt value when generating login links/tokens.

    Default: "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.

    Default: "remember-salt".

SECURITY_TWO_FACTOR_VALIDITY_SALT
    Specifies the salt value when generating two factor validity tokens.

    Default: "tf-validity-salt".

SECURITY_US_SETUP_SALT
    Default: "us-setup-salt"

SECURITY_EMAIL_PLAINTEXT
    Sends email as plaintext using *.txt template.

    Default: True.

SECURITY_EMAIL_HTML
    Sends email as HTML using *.html template.

    Default: True.

SECURITY_CLI_USERS_NAME
    Specifies the name for the command managing users. Disable by setting False.

    Default: users.

SECURITY_CLI_ROLES_NAME
    Specifies the name for the command managing roles. Disable by setting False.

    Default: roles.

SECURITY_JOIN_USER_ROLES
    Specifies whether to set the UserModel.roles loading relationship to joined when a roles attribute is present for a SQLAlchemy Datastore. Setting this to False restores pre 3.3.0 behavior and is required if the roles attribute is not a joinable attribute on the UserModel. The default setting improves performance by only requiring a single DB call.

    Default: True.

    New in version 3.4.0.

  Login/Logout

SECURITY_LOGIN_URL
    Specifies the login URL.

    Default: "/login".

SECURITY_LOGOUT_URL
    Specifies the logout URL.

    Default:"/logout".

SECURITY_LOGOUT_METHODS
    Specifies the HTTP request methods that the logout URL accepts. Specify None to disable the logout URL (and implement your own). Configuring with just ["POST"] is slightly more secure. The default includes "GET" for backwards compatibility.

    Default: ["GET", "POST"].

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 the Flask config APPLICATION_ROOT value which itself defaults to "/".

    Default: APPLICATION_ROOT.

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 the Flask config APPLICATION_ROOT value which itself defaults to "/".

    Default: APPLICATION_ROOT.

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.

    Default: None.

SECURITY_LOGIN_USER_TEMPLATE
    Specifies the path to the template for the user login page.

    Default: "security/login_user.html".

SECURITY_VERIFY_URL
    Specifies the re-authenticate URL. If SECURITY_FRESHNESS evaluates to < 0; this endpoint won't be registered.

    Default: "/verify"

SECURITY_VERIFY_TEMPLATE
    Specifies the path to the template for the verify password page.

    Default: "security/verify.html".

SECURITY_POST_VERIFY_URL
    Specifies the default view to redirect to after a user successfully re-authenticates either via the SECURITY_VERIFY_URL or the SECURITY_US_VERIFY_URL. Normally this won't need to be set and after the verification/re-authentication, the referring view (held in the next parameter) will be redirected to.

    Default: None.

  Registerable

SECURITY_REGISTERABLE
    Specifies if Flask-Security should create a user registration endpoint.

    Default: False

SECURITY_SEND_REGISTER_EMAIL
    Specifies whether registration email is sent.

    Default: True.

SECURITY_EMAIL_SUBJECT_REGISTER
    Sets the subject for the confirmation email.

    Default: Welcome.

SECURITY_REGISTER_USER_TEMPLATE
    Specifies the path to the template for the user registration page.

    Default: security/register_user.html.

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.

    Default: None.

SECURITY_REGISTER_URL
    Specifies the register URL.

    Default: "/register".

  Confirmable

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.

    Default: False.

SECURITY_CONFIRM_EMAIL_WITHIN
    Specifies the amount of time a user has before their confirmation link expires. Always pluralize the time unit for this value.

    Default: 5 days.

SECURITY_CONFIRM_URL
    Specifies the email confirmation URL.

    Default: "/confirm".

SECURITY_SEND_CONFIRMATION_TEMPLATE
    Specifies the path to the template for the resend confirmation instructions page.

    Default: security/send_confirmation.html.

SECURITY_EMAIL_SUBJECT_CONFIRM
    Sets the subject for the email confirmation message.

    Default: Please confirm your email.

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.

    Default: 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.

    Default: None.

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.

    Default: True.

SECURITY_LOGIN_WITHOUT_CONFIRMATION
    Specifies if a user may login before confirming their email when the value of SECURITY_CONFIRMABLE is set to True.

    Default: False.

SECURITY_REQUIRES_CONFIRMATION_ERROR_VIEW
    Specifies a redirect page if the users tries to login, reset password or us-signin with an unconfirmed account. If an URL endpoint is specified, flashes an error messages and passes user email as an argument. For us-signin, no argument is specified: it simply flashes the error message and redirects. Default behavior is to reload the form with an error message without redirecting to an other page.

    Default: None.

  Changeable

Configuration variables for the SECURITY_CHANGEABLE feature:

SECURITY_CHANGEABLE
    Specifies if Flask-Security should enable the change password endpoint.

    Default: False.

SECURITY_CHANGE_URL
    Specifies the password change URL.

    Default: "/change".

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.

    Default: None.

SECURITY_CHANGE_PASSWORD_TEMPLATE
    Specifies the path to the template for the change password page.

    Default: security/change_password.html.

SECURITY_SEND_PASSWORD_CHANGE_EMAIL
    Specifies whether password change email is sent.

    Default: True.

SECURITY_EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE
    Sets the subject for the password change notice.

    Default: Your password has been changed.

  Recoverable

SECURITY_RECOVERABLE
    Specifies if Flask-Security should create a password reset/recover endpoint.

    Default: False.

SECURITY_RESET_URL
    Specifies the password reset URL.

    Default: "/reset".

SECURITY_RESET_PASSWORD_TEMPLATE
    Specifies the path to the template for the reset password page.

    Default: security/reset_password.html.

SECURITY_FORGOT_PASSWORD_TEMPLATE
    Specifies the path to the template for the forgot password page.

    Default: security/forgot_password.html.

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.

    Default: 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.

    Default: 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.

    Default: None.

SECURITY_RESET_PASSWORD_WITHIN
    Specifies the amount of time a user has before their password reset link expires. Always pluralize the time unit for this value.

    Default: 5 days.

SECURITY_SEND_PASSWORD_RESET_EMAIL
    Specifies whether password reset email is sent. These are instructions including a link that can be clicked on.

    Default: True.

SECURITY_SEND_PASSWORD_RESET_NOTICE_EMAIL
    Specifies whether password reset notice email is sent. This is sent once a user's password was successfully reset.

    Default: True.

SECURITY_EMAIL_SUBJECT_PASSWORD_RESET
    Sets the subject for the password reset email.

    Default: Password reset instructions.

SECURITY_EMAIL_SUBJECT_PASSWORD_NOTICE
    Sets subject for the password notice.

    Default: Your password has been reset.

  Two-Factor

Configuration related to the two-factor authentication feature.

New in version 3.2.0.

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.

    Default: False.

SECURITY_TWO_FACTOR_REQUIRED
    If set to True then all users will be required to setup and use two factor authorization.

    Default: False.

SECURITY_TWO_FACTOR_ENABLED_METHODS
    Specifies the default enabled methods for two-factor authentication.

    Default: ['email', 'authenticator', 'sms'] which are the only currently supported methods.

SECURITY_TWO_FACTOR_SECRET
    Deprecated since version 3.4.0: see: SECURITY_TOTP_SECRETS

SECURITY_TWO_FACTOR_URI_SERVICE_NAME
    Deprecated since version 3.4.0: see: SECURITY_TOTP_ISSUER

SECURITY_TWO_FACTOR_SMS_SERVICE
    Deprecated since version 3.4.0: see: SECURITY_SMS_SERVICE

SECURITY_TWO_FACTOR_SMS_SERVICE_CONFIG
    Deprecated since version 3.4.0: see: SECURITY_SMS_SERVICE_CONFIG

SECURITY_TWO_FACTOR_AUTHENTICATOR_VALIDITY
    Specifies the number of seconds access token is valid.

    Default: 120.

SECURITY_TWO_FACTOR_MAIL_VALIDITY
    Specifies the number of seconds access token is valid.

    Default: 300.

SECURITY_TWO_FACTOR_SMS_VALIDITY
    Specifies the number of seconds access token is valid.

    Default: 120.

SECURITY_TWO_FACTOR_RESCUE_MAIL
    Specifies the email address users send mail to when they can't complete the two-factor authentication login.

    Default: no-reply@localhost.

SECURITY_EMAIL_SUBJECT_TWO_FACTOR
    Sets the subject for the two factor feature.

    Default: Two-factor Login

SECURITY_EMAIL_SUBJECT_TWO_FACTOR_RESCUE
    Sets the subject for the two factor help function.

    Default: Two-factor Rescue

SECURITY_TWO_FACTOR_VERIFY_CODE_TEMPLATE
    Specifies the path to the template for the verify code page for the two-factor authentication process.

    Default: 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.

    Default: security/two_factor_setup.html.

SECURITY_TWO_FACTOR_SETUP_URL
    Specifies the two factor setup URL.

    Default: "/tf-setup".

SECURITY_TWO_FACTOR_TOKEN_VALIDATION_URL
    Specifies the two factor token validation URL.

    Default: "/tf-validate".

SECURITY_TWO_FACTOR_RESCUE_URL
    Specifies the two factor rescue URL.

    Default: "/tf-rescue".

SECURITY_TWO_FACTOR_ALWAYS_VALIDATE
    Specifies whether the application should require a two factor code upon every login. If set to False then the 2 values below are used to determine when a code is required. Note that this is cookie based - so a new browser session will always require a fresh two-factor code.

    Default: True.

SECURITY_TWO_FACTOR_LOGIN_VALIDITY
    Specifies the expiration of the two factor validity cookie and verification of the token.

    Default: 30 Days.

SECURITY_TWO_FACTOR_VALIDITY_COOKIE
    A dictionary containing the parameters of the two factor validity cookie. The complete set of parameters is described in Flask's set_cookie documentation.

    Default: {'httponly': True, 'secure': False, 'samesite': None}.

  Unified Signin
New in version 3.4.0.

SECURITY_UNIFIED_SIGNIN
    To enable this feature - set this to True.

    Default: False

SECURITY_US_SIGNIN_URL
    Sign in a user with an identity and a passcode.

    Default: "/us-signin"

SECURITY_US_SIGNIN_SEND_CODE_URL
    Endpoint that given an identity, and a previously setup authentication method, will generate and return a one time code. This isn't necessary when using an authenticator app.

    Default: "/us-signin/send-code"

SECURITY_US_SETUP_URL
    Endpoint for setting up and validating SMS or an authenticator app for use in receiving one-time codes.

    Default: "/us-setup"

SECURITY_US_VERIFY_LINK_URL
    This endpoint handles the 'magic link' that is sent when the user requests a code via email. It is mostly just accessed via a GET from an email reader.

    Default: "/us-verify-link"

SECURITY_US_VERIFY_URL
    This endpoint handles re-authentication, the caller must be already authenticated and then enter in their primary credentials (password/passcode) again. This is used when an endpoint (such as /us-setup) fails freshness checks. This endpoint won't be registered if SECURITY_FRESHNESS evaluates to < 0.

    Default: "/us-verify"

SECURITY_US_VERIFY_SEND_CODE_URL
    As part of /us-verify, this endpoint will send the appropriate code. This endpoint won't be registered if SECURITY_FRESHNESS evaluates to < 0.

    Default: "/us-verify/send-code"

SECURITY_US_POST_SETUP_VIEW
    Specifies the view to redirect to after a user successfully setups an authentication method (non-json). 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.

    Default: None

SECURITY_US_SIGNIN_TEMPLATE
    Default: "security/us_signin.html"

SECURITY_US_SETUP_TEMPLATE
    Default: "security/us_setup.html"

SECURITY_US_VERIFY_TEMPLATE
    Default: "security/us_verify.html"

SECURITY_US_ENABLED_METHODS
    Specifies the default enabled methods for unified sign in authentication. Be aware that password only affects this SECURITY_US_SIGNIN_URL endpoint. Removing it from here won't stop users from using the SECURITY_LOGIN_URL endpoint.

    If you select sms then make sure you add this to SECURITY_USER_IDENTITY_ATTRIBUTES:

    {"us_phone_number": {"mapper": uia_phone_mapper}},

    Default: ["password", "email", "authenticator", "sms"] - which are the only supported options.

SECURITY_US_MFA_REQUIRED
    A list of US_ENABLED_METHODS that will require two-factor authentication. This is of course dependent on the settings of SECURITY_TWO_FACTOR and SECURITY_TWO_FACTOR_REQUIRED. Note that even with REQUIRED, only methods listed here will trigger a two-factor cycle.

    Default: ["password", "email"].

SECURITY_US_TOKEN_VALIDITY
    Specifies the number of seconds access token/code is valid.

    Default: 120

SECURITY_US_EMAIL_SUBJECT
    Sets the email subject when sending the verification code via email.

    Default: _("Verification Code")

SECURITY_US_SETUP_WITHIN
    Specifies the amount of time a user has before their setup token expires. Always pluralize the time unit for this value.

    Default: "30 minutes"

SECURITY_US_SIGNIN_REPLACES_LOGIN
    If set, then the SECURITY_LOGIN_URL will be registered to the us-signin endpoint. Doing this will mean that logout will properly redirect to the us-signin endpoint.

    Default: False

    Additional relevant configuration variables:

•   SECURITY_USER_IDENTITY_ATTRIBUTES - Defines the order and methods for parsing and validating identity.

•   SECURITY_DEFAULT_REMEMBER_ME

•   SECURITY_SMS_SERVICE - When SMS is enabled in SECURITY_US_ENABLED_METHODS.

•   SECURITY_SMS_SERVICE_CONFIG

•   SECURITY_TOTP_SECRETS

•   SECURITY_TOTP_ISSUER

•   SECURITY_PHONE_REGION_DEFAULT

•   SECURITY_LOGIN_ERROR_VIEW - The user is redirected here if SECURITY_US_VERIFY_LINK_URL has an error and the request is json and SECURITY_REDIRECT_BEHAVIOR equals "spa".

•   SECURITY_FRESHNESS - Used to protect /us-setup.

•   SECURITY_FRESHNESS_GRACE_PERIOD - Used to protect /us-setup.

  Passwordless

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 being replaced with a more generalized passwordless feature that includes using SMS or authenticator applications for generating codes.

    Default: False.

SECURITY_SEND_LOGIN_TEMPLATE
    Specifies the path to the template for the send login instructions page for passwordless logins.

    Default:security/send_login.html.

SECURITY_EMAIL_SUBJECT_PASSWORDLESS
    Sets the subject for the passwordless feature.

    Default: Login instructions.

SECURITY_LOGIN_WITHIN
    Specifies the amount of time a user has before a login link expires. Always pluralize the time unit for this value.

    Default: 1 days.

SECURITY_LOGIN_ERROR_VIEW
    Specifies the view/URL to redirect to after a GET passwordless link or GET unified sign in magic link when there is an error. This is only valid if SECURITY_REDIRECT_BEHAVIOR == spa. Query params in the redirect will contain the error.

    Default: None.

  Trackable

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.

    Default: False

  Feature Flags

All feature flags. By default all are 'False'/not enabled.

•   SECURITY_CONFIRMABLE

•   SECURITY_REGISTERABLE

•   SECURITY_RECOVERABLE

•   SECURITY_TRACKABLE

•   SECURITY_PASSWORDLESS

•   SECURITY_CHANGEABLE

•   SECURITY_TWO_FACTOR

•   SECURITY_UNIFIED_SIGNIN

  URLs and Views

A list of all URLs and Views:

•   SECURITY_LOGIN_URL

•   SECURITY_LOGOUT_URL

•   SECURITY_VERIFY_URL

•   SECURITY_REGISTER_URL

•   SECURITY_RESET_URL

•   SECURITY_CHANGE_URL

•   SECURITY_CONFIRM_URL

•   SECURITY_TWO_FACTOR_SETUP_URL

•   SECURITY_TWO_FACTOR_TOKEN_VALIDATION_URL

•   SECURITY_TWO_FACTOR_RESCUE_URL

•   SECURITY_POST_LOGIN_VIEW

•   SECURITY_POST_LOGOUT_VIEW

•   SECURITY_CONFIRM_ERROR_VIEW

•   SECURITY_POST_REGISTER_VIEW

•   SECURITY_POST_CONFIRM_VIEW

•   SECURITY_POST_RESET_VIEW

•   SECURITY_POST_CHANGE_VIEW

•   SECURITY_UNAUTHORIZED_VIEW

•   SECURITY_RESET_VIEW

•   SECURITY_RESET_ERROR_VIEW

•   SECURITY_LOGIN_ERROR_VIEW

•   SECURITY_US_SIGNIN_URL

•   SECURITY_US_SETUP_URL

•   SECURITY_US_SIGNIN_SEND_CODE_URL

•   SECURITY_US_VERIFY_LINK_URL

•   SECURITY_US_VERIFY_URL

•   SECURITY_US_VERIFY_SEND_CODE_URL

•   SECURITY_US_POST_SETUP_VIEW

  Template Paths

A list of all templates:

•   SECURITY_FORGOT_PASSWORD_TEMPLATE

•   SECURITY_LOGIN_USER_TEMPLATE

•   SECURITY_VERIFY_TEMPLATE

•   SECURITY_REGISTER_USER_TEMPLATE

•   SECURITY_RESET_PASSWORD_TEMPLATE

•   SECURITY_CHANGE_PASSWORD_TEMPLATE

•   SECURITY_SEND_CONFIRMATION_TEMPLATE

•   SECURITY_SEND_LOGIN_TEMPLATE

•   SECURITY_TWO_FACTOR_VERIFY_CODE_TEMPLATE

•   SECURITY_TWO_FACTOR_SETUP_TEMPLATE

•   SECURITY_US_SIGNIN_TEMPLATE

•   SECURITY_US_SETUP_TEMPLATE

•   SECURITY_US_VERIFY_TEMPLATE

  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_API_ERROR

•   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_FAILED_TO_SEND_CODE

•   SECURITY_MSG_FORGOT_PASSWORD

•   SECURITY_MSG_IDENTITY_ALREADY_ASSOCIATED

•   SECURITY_MSG_INVALID_CODE

•   SECURITY_MSG_INVALID_CONFIRMATION_TOKEN

•   SECURITY_MSG_INVALID_EMAIL_ADDRESS

•   SECURITY_MSG_INVALID_LOGIN_TOKEN

•   SECURITY_MSG_INVALID_PASSWORD

•   SECURITY_MSG_INVALID_PASSWORD_CODE

•   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_BREACHED

•   SECURITY_MSG_PASSWORD_BREACHED_SITE_ERROR

•   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_PASSWORD_TOO_SIMPLE

•   SECURITY_MSG_PHONE_INVALID

•   SECURITY_MSG_REAUTHENTICATION_REQUIRED

•   SECURITY_MSG_REAUTHENTICATION_SUCCESSFUL

•   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_PERMISSION_DENIED

•   SECURITY_MSG_TWO_FACTOR_METHOD_NOT_AVAILABLE

•   SECURITY_MSG_TWO_FACTOR_DISABLED

•   SECURITY_MSG_UNAUTHORIZED

•   SECURITY_MSG_UNAUTHENTICATED

•   SECURITY_MSG_US_METHOD_NOT_AVAILABLE

•   SECURITY_MSG_US_SETUP_EXPIRED

•   SECURITY_MSG_US_SETUP_SUCCESSFUL

•   SECURITY_MSG_US_SPECIFY_IDENTITY

•   SECURITY_MSG_USE_CODE

•   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.

NOTE: The below quickstarts are just that - they don't enable most of the features (such as registration, reset, etc.). They basically create a single user, and you can login as that user... that's it. As you add more features, additional packages (e.g. Flask-Mail, Flask-Babel, pyqrcode) might be required and will need to be added to your requirements.txt (or equivalent) file. Flask-Security does some configuration validation and will output error messages to the console for some missing packages.

NOTE: The default SECURITY_PASSWORD_HASH is "bcrypt" - so be sure to install bcrypt. If you opt for a different hash e.g. "argon2" you will need to install a different package.

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 bcrypt

  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_v2 as 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()
    if not user_datastore.find_user(email="test@me.com"):
        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 bcrypt

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()
        if not user_datastore.find_user(email="test@me.com"):
            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')
    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), unique=True, nullable=True)
        password = Column(String(255), nullable=False)
        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), unique=True, nullable=False)
        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 bcrypt

  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)
    permissions = 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=64, unique=True)
    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():
    if not user_datastore.find_user(email="test@me.com"):
        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()

  Basic Peewee Application

  Peewee Install requirements

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

  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(RoleMixin, db.Model):
    name = CharField(unique=True)
    description = TextField(null=True)
    permissions = TextField(null=True)
# N.B. order is important since db.Model also contains a get_id() -
# we need the one from UserMixin.
class User(UserMixin, db.Model):
    email = TextField()
    password = TextField()
    active = BooleanField(default=True)
    fs_uniquifier = TextField(null=False)
    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)
    def get_permissions(self):
        return self.role.get_permissions()
# 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)
    if not user_datastore.find_user(email="test@me.com"):
        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 an outgoing mail service via the mail_util_cls which is part of initial configuration. The default class flask_security.MailUtil utilizes the Flask-Mail package. Be sure to add flask_mail to your requirements.txt.

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'] = False
app.config['MAIL_USE_TLS'] = 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.

    You also might want to set the following configurations in your conftest.py:

    app.config["WTF_CSRF_ENABLED"] = False
    # Our test emails/domain isn't necessarily valid
    app.config["SECURITY_EMAIL_VALIDATOR_ARGS"] = {"check_deliverability": False}
    # Make this plaintext for most tests - reduces unit test time by 50%
    app.config["SECURITY_PASSWORD_HASH"] = "plaintext"

  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 list of 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).

The provided models are versioned since they represent actual DB models, and any changes require a schema migration (and perhaps a data migration). Applications must specifically import the version they want (and handle any required migration).

Your User model needs a Primary Key - Flask-Security doesn't actually reference this - so it can be any name or type your application needs. It should be used in the foreign relationship between User and Role.

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

User

•   primary key

•   email (for most features - unique, non-nullable)

•   password (non-nullable)

•   active (boolean, non-nullable)

•   fs_uniquifier (unique, non-nullable)

    Role

•   primary key

•   name (unique, non-nullable)

•   description (string)

  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 (datetime)

  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 (datetime)

•   current_login_at (datetime)

•   last_login_ip (string)

•   current_login_ip (string)

•   login_count (integer)

  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 (string)

•   tf_primary_method (string)

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

•   tf_phone_number (string)

  Unified Sign In

If you enable unified sign in by setting your application's SECURITY_UNIFIED_SIGNIN configuration value to True, your User model will require the following additional fields:

•   us_totp_secrets (an arbitrarily long Text field)

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

•   us_phone_number (string)

  Separate Identity Domains

If you want authentication tokens to not be invalidated when the user changes their password add the following to your User model:

•   fs_token_uniquifier (unique, non-nullable)

  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 (UnicodeText)

  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):
        rv = super().get_security_payload()
        # :meth:`User.calc_username`
        rv["username"] = self.calc_username()
        rv["confirmation_needed"] = self.confirmed_at is None
        return rv

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/verify.html

•   security/two_factor_setup.html

•   security/two_factor_verify_code.html

•   security/us_signin.html

•   security/us_setup.html

•   security/us_verify.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

•   us_signin_context_processor: Unified sign in view

•   us_setup_context_processor: Unified sign in setup 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, only fields that exist in the user model are passed (as kwargs) to UserDatastore.create_user(). Thus, in the above case, the first_name and last_name fields will only be passed if the model looks 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_rescue_form: Two-factor help user form

•   us_signin_form: Unified sign in form

•   us_setup_form: Unified sign in setup form

•   us_setup_validate_form: Unified sign in setup validation form

    TIP: Changing/extending the form class won't directly change how it is displayed. You need to ALSO provide your own template and explicitly adds the new fields you want displayed.

  Localization

All messages, form labels, and form strings are localizable. Flask-Security uses Flask-Babel or Flask-BabelEx to manage its messages. All translations are tagged with a domain, as specified by the configuration variable SECURITY_I18N_DOMAIN (default: "flask_security"). For messages and labels all this works seamlessly. For strings inside templates it is necessary to explicitly ask for the "flask_security" domain, since your application itself might have its own domain. Flask-Security places the method _fsdomain in jinja2's global environment and uses that in all templates. 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") }}

Be aware that Flask-Security will validate and normalize email input using the email_validator package. The normalized form is stored in the DB.

  Overriding Messages

It is possible to change one or more messages (either the original default english and/or a specific translation). Adding the following to your app:

app.config["SECURITY_MSG_INVALID_PASSWORD"] = ("Password no-worky", "error")

Will change the default message in english.

TIP: The string messages themselves are a 'key' into the translation .po/.mo files. Do not pass in gettext('string') or lazy_gettext('string).

If you need translations then you need to create your own translations directory and add the appropriate .po files and compile them. Finally, add your translations directory path to the configuration. In this example, create a file flask_security.po under a directory: translations/fr_FR/LC_MESSAGES (for french) with the following contents:

msgid ""
msgstr ""
msgid "Password no-worky"
msgstr "Passe - no-worky"

Then compile it with:

pybabel compile -d translations/ -i translations/fr_FR/LC_MESSAGES/flask_security.po -l fr_FR

Finally add your translations directory to your configuration:

app.config["SECURITY_I18N_DIRNAME"] = [pkg_resources.resource_filename("flask_security", "translations"), "translations"]

  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/reset_notice.txt

•   security/email/change_notice.txt

•   security/email/change_notice.html

•   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

•   security/email/us_instructions.html

•   security/email/us_instructions.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")

    There are many configuration variables associated with emails, and each template will receive a slightly different context. The Gate Config column are configuration variables that if set to False will bypass sending of the email (they all default to True). In most cases, in addition to an email being sent, a Signal is sent. The table below summarizes all this:
    Template Name | Gate Config | Subject Config | Context Vars | Signal Sent
    welcome | SECURITY_SEND_REGISTER_EMAIL | SECURITY_EMAIL_SUBJECT_REGISTER | user confirmation_link | user_registered
    confirmation_instructions | N/A | SECURITY_EMAIL_SUBJECT_CONFIRM | user confirmation_link | confirm_instructions_sent
    login_instructions | N/A | SECURITY_EMAIL_SUBJECT_PASSWORDLESS | user login_link | login_instructions_sent
    reset_instructions | SEND_PASSWORD_RESET_EMAIL | SECURITY_EMAIL_SUBJECT_PASSWORD_RESET | user reset_link | reset_password_instructions_sent
    reset_notice | SEND_PASSWORD_RESET_NOTICE_EMAIL | SECURITY_EMAIL_SUBJECT_PASSWORD_NOTICE | user | password_reset
    change_notice | SEND_PASSWORD_CHANGE_EMAIL | SECURITY_EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE | user | password_changed
    two_factor_instructions | N/A | SECURITY_EMAIL_SUBJECT_TWO_FACTOR | user token username | tf_security_token_sent
    two_factor_rescue | N/A | SECURITY_EMAIL_SUBJECT_TWO_FACTOR_RESCUE | user | N/A
    us_instructions | N/A | SECURITY_US_EMAIL_SUBJECT | user token login_link username | us_security_token_sent

    When sending an email, Flask-Security goes through the following steps:

1.  Calls the email context processor as described above

2.  Calls render_template (as configured at Flask-Security initialization time) with the context and template to produce a text and/or html version of the message

3.  Calls MailUtil.send_mail() with all the required parameters.

    The default implementation of MailUtil.send_mail uses Flask-Mail to create and send the message. By providing your own implementation, you can use any available python email handling package.

    Email subjects are by default localized - see above section on Localization to learn how to customize them.

  Emails with Celery

Sometimes it makes sense to send emails via a task queue, such as Celery. This is supported by providing your own implementation of the MailUtil class:

from flask_security import MailUtil
class MyMailUtil(MailUtil):
    def send_mail(self, template, subject, recipient, sender, body, html, user, **kwargs):
        send_flask_mail.delay(
            subject=subject,
            sender=sender,
            recipients=[recipient],
            body=body,
            html=html,
        )

Then register your class as part of Flask-Security initialization:

from flask import Flask
from flask_mail import Mail, Message
from flask_security import Security, SQLAlchemyUserDatastore
from celery import Celery
mail = Mail()
security = Security()
celery = Celery()
@celery.task
def send_flask_mail(**kwargs):
    # If you use Flask_Mail - it needs an app context
    with app.app_context():
        mail.send(Message(**kwargs))
def create_app(config):
    """Initialize Flask instance."""
    app = Flask(__name__)
    app.config.from_object(config)
    mail.init_app(app)
    datastore = SQLAlchemyUserDatastore(db, User, Role)
    security.init_app(app, datastore, mail_util_cls=MyMailUtil)
    return app

  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(). 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, behave as follows:

•   If authentication fails as the result of a @login_required, @auth_required("session", "token"), 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 authentication fails as the result of a @http_auth_required or @auth_required("basic") then a 401 is returned along with the http header WWW-Authenticate set to Basic realm="xxxx". The realm name is defined by SECURITY_DEFAULT_HTTP_AUTH_REALM.

•   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 an Authenticator app such as Google, Lastpass, or Authy.

The following code sample illustrates how to get started as quickly as possible using SQLAlchemy and two-factor feature. In this example both email and an authenticator app is supported as a second factor. See below for information about SMS.

  Basic SQLAlchemy Two-Factor Application

  SQLAlchemy Install requirements

$ mkvirtualenv <your-app-name>
$ pip install flask-security-too flask-sqlalchemy flask-mail bcrypt 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, auth_required
from flask_mail import Mail
# 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'] = ['email',
  'authenticator']  # 'sms' also valid but requires an sms provider
app.config['SECURITY_TWO_FACTOR'] = True
app.config['SECURITY_TWO_FACTOR_RESCUE_MAIL'] = "put_your_mail@gmail.com"
app.config['SECURITY_TWO_FACTOR_ALWAYS_VALIDATE'] = False
app.config['SECURITY_TWO_FACTOR_LOGIN_VALIDITY'] = "1 week"
# Generate a good totp secret using: passlib.totp.generate_secret()
app.config['SECURITY_TOTP_SECRETS'] = {"1": "TjQ9Qa31VOrfEzuPy4VHQWPCTmRzCnFzMKLxXYiZu9B"}
app.config['SECURITY_TOTP_ISSUER'] = "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)
    # Make username unique but not required.
    username = Column(String(255), unique=True, nullable=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(128), nullable=True)
    tf_primary_method = db.Column(db.String(64), nullable=True)
    tf_totp_secret = db.Column(db.String(255), nullable=True)
# 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()
    if not user_datastore.find_user(email='gal@lp.com'):
        user_datastore.create_user(email='gal@lp.com', password='password', username='gal')
    db.session.commit()
# Views
@app.route('/')
@auth_required
def home():
    return render_template('index.html')
if __name__ == '__main__':
    app.run()

  Adding SMS

Using SMS as a second factor requires access to an SMS service provider such as "Twilio". Flask-Security supports Twilio out of the box. For other sms service providers you will need to subclass SmsSenderBaseClass and register it:

SmsSenderFactory.senders[<service-name>] = <service-class>

You need to install additional packages:

pip install phonenumberslite twilio

And set additional configuration variables:

app.config["SECURITY_TWO_FACTOR_ENABLED_METHODS"] = ['email',
  'authenticator', 'sms']
app.config["SECURITY_SMS_SERVICE"] = "Twilio"
app.config["SECURITY_SMS_SERVICE_CONFIG" =
  {'ACCOUNT_SID': <from twilio>, 'AUTH_TOKEN': <from twilio>, 'PHONE_NUMBER': <from twilio>}

  Theory of Operation

NOTE: The Two-factor feature requires that session cookies be received and sent as part of the API. This is true regardless of whether the application uses forms or JSON.

The Two-factor (2FA) API has four paths:

•   Normal login once everything set up

•   Changing 2FA setup

•   Initial login/registration when 2FA is required

•   Rescue

    When using forms, the flow from one state to the next is handled by the forms themselves. When using JSON the application must of course explicitly access the appropriate endpoints. The descriptions below describe the JSON access pattern.

  Normal Login

In the normal case, when the user has already setup their preferred 2FA method (e.g. email, SMS, authenticator app), then the flow starts with the authentication process using the /login or /us-signin endpoints, providing their identity and password. If 2FA is required, the response will indicate that. Then, the application must POST to the /tf-validate with the correct code.

  Changing 2FA Setup

An authenticated user can change their 2FA configuration (primary_method, phone number, etc.). In order to prevent a user from being locked out, the new configuration must be validated before it is stored permanently. The user starts with a GET on /tf-setup. This will return a list of configured 2FA methods the user can choose from, and the existing configuration. This must be followed with a POST on /tf-setup with the new primary method (and phone number if SMS). In the case of SMS, a code will be sent to the phone/device and again use /tf-validate to confirm code. In the case of setting up an authenticator app, the response to the POST will contain the QRcode image as well as the required information for manual entry. Once the code has been successfully entered, the new configuration will be permanently stored.

  Initial login/registration

This is basically a combination of the above two - initial POST to /login will return indicating that 2FA is required. The user must then POST to /tf_setup to setup the desired 2FA method, and finally have the user enter the code and POST to /tf-validate.

  Rescue

Life happens - if the user doesn't have their mobile devices (SMS) or authenticator app, then they can request using /tf-rescue endpoint to have the code sent to their email. If they have lost access to their email, they can request an email be sent to the application administrators.

  Validity

Sometimes it can be preferable to enter the 2FA code once a day/week/month, especially if a user logs in and out of a website multiple times. This allows the security of a two factor authentication but with a slightly better user experience. This can be achieved by setting SECURITY_TWO_FACTOR_ALWAYS_VALIDATE to False, and clicking the 'Remember' button on the login form. Once the two factor code is validated, a cookie is set to allow skipping the validation step. The cookie is named tf_validity and contains the signed token containing the user's fs_uniquifier. The cookie and token are both set to expire after the time delta given in SECURITY_TWO_FACTOR_LOGIN_VALIDITY. Note that setting SECURITY_TWO_FACTOR_LOGIN_VALIDITY to 0 is equivalent to SECURITY_TWO_FACTOR_ALWAYS_VALIDATE being True.

  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. The external (json/form) API is described here

•   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
SECURITY_UNIFIED_SIGNIN = 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()

•   auth_token_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')

  A note about Basic Auth

Basic Auth is supported in Flask-Security, using the @http_auth_required() decorator. If a request for an endpoint protected with @http_auth_required is received, and the request doesn't contain the appropriate HTTP Headers, a 401 is returned along with the required WWW-Authenticate header. In this case there won't be a usable session cookie returned so all future requests will also require credentials to be sent. Effectively the caller is temporarily 'logged in' at the beginning of each request and 'logged out' again at the end of the request. Most (all?) browsers intercept this response and pop up a login dialog box and remember, for the site, the entered credentials. This effectively bypasses any of the normal Flask-Security login forms. By default, the Flask-Security endpoints that require the caller be authenticated do NOT support basic - however the SECURITY_API_ENABLED_METHODS can be used to override this.

  Freshness

A common pattern for browser-based sites is to use sessions to manage identity. This is usually implemented using session cookies. These cookies expire once the session (browser tab) is closed. This is very convenient, and keeps the users from having to constantly re-authenticate. The downside is that sessions can easily be open for days or weeks. This adds to the security risk that some bad-actor or XSS gets control of the browser and then can do anything the user can. To mitigate that, operations that change fundamental identity characteristics (such as email, password, etc.) can be protected by requiring a 'fresh' or recent authentication. Flask-Security supports this with the following:

•   auth_required() takes parameters that define how recent the authentication must have happened. In addition a grace period can be specified so that multiple step operations don't require re-authentication in the middle.

•   A default Security.reauthn_handler() that is called when a request fails the recent authentication check.

•   SECURITY_VERIFY_URL and SECURITY_US_VERIFY_URL endpoints that request the user to re-authenticate.

•   VerifyForm and UsVerifyForm forms that can be extended.

    Flask-Security itself uses this as part of securing the unified-sign-in and two-factor setup endpoints.

  Password Validation and Complexity

There is a large body of references (and endless discussions) around how to get users to create good passwords. The OWASP Authenication cheatsheet is a useful place to start. Flask-Security has a default password validator that:

•   Checks for minimum and maximum length (minimum is configurable via SECURITY_PASSWORD_LENGTH_MIN). The default is 8 characters as defined by NIST.

•   If SECURITY_PASSWORD_CHECK_BREACHED is set, will use the API for haveibeenpwned to check if the password is on a list of breached passwords. The configuration variable SECURITY_PASSWORD_BREACHED_COUNT can be used to set the minimum allowable 'breaches'.

•   If SECURITY_PASSWORD_COMPLEXITY_CHECKER is set to zxcvbn and the package zxcvbn is installed, it will check the password for complexity.

    Be aware that zxcvbn is not actively being maintained, and has localization issues.

    In addition to validation, unicode passwords should be normalized as specified by NIST requirement: 5.1.1.2 Memorized Secret Verifiers. Normalization can be disabled by setting the SECURITY_PASSWORD_NORMALIZE_FORM to None. Validation and normalization is encapsulated in PasswordUtil. This can be overridden by passing your class at app initialization time. The PasswordUtil.validate() is passed additional kwargs to allow custom validators more flexibility. A custom validator can still call the underlying methods where appropriate: flask_security.password_length_validator(), flask_security.password_complexity_validator(), and flask_security.password_breached_validator().

  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 CSRF 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

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

  User Object Helpers

  Datastores

  Utils

  Signals

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

TIP: Remember to add **extra_args to your signature so that if we add additional parameters in the future your code doesn't break.

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_authenticated
    Sent when a user successfully authenticates. In addition to the app (which is the sender), it is passed user, and authn_via arguments. The authn_via argument specifies how the user authenticated - it will be a list with possible values of password, sms, authenticator, email, confirm, reset, register.

    New in version 3.4.0.

user_registered
    Sent when a user registers on the site. In addition to the app (which is the sender), it is passed user, confirm_token and form_data arguments. form_data is a dictionary representation of registration form's content received with registration request.

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.

    New in version 3.3.0.

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.

    New in version 3.3.0.

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

    New in version 3.3.0.

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.

    New in version 3.3.0.

us_security_token_sent
    Sent when a unified sign in access code is sent. In addition to the app (which is the sender), it is passed user, method, token, phone_number, and send_magic_link arguments.

    New in version 3.4.0.

us_profile_changed
    Sent when user completes changing their unified sign in profile. In addition to the app (which is the sender), it is passed user and method arguments.

    New in version 3.4.0.

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/Flask-Middleware/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 -r requirements/tests.txt

7.  Install pre-commit hooks:

    $ pre-commit install

8.  Develop the Feature/Bug Fix and edit

9.  Write Tests for your code in:

    tests/

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

    $ pytest tests
    $ pre-commit run --all-files
    $ python setup.py build_sphinx compile_catalog

11. Use tox:

    $ tox  # run everything CI does
    $ tox -e py38-low  # make sure works with older dependencies
    $ tox -e style  # run pre-commit/style checks

12. 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

13. Submit a pull request through the GitHub website.

14. 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 -p 8081

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.

  Updating Translations

If you change any translatable strings (such as new messages, modified forms, etc.) you need to re-generate the translations:

$ python setup.py extract_messages
$ python setup.py update_catalog
$ python setup.py compile_catalog

  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
pytest --realdburl postgres://<user>@localhost/
# For mysql
pytest --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 4.0.1

Released April 2, 2021

  Features

  Fixes

•   (#461) 4.0 doesn't accept 3.4 authentication tokens. (kuba-lilz)

•   (#460) 2-fa error: Failed to send code - improved documentation and debuggability.

•   (#454) 2-fa error: TypeError - fixed documentation.

•   (#443) Calling create user without any arguments - fixed underlying cause of translating form errors in the CLI.

•   (#442) Email validation confusion - added documentation.

•   (#450) Add documentation on how to override specific error messages.

•   (#439) Don't install global-scope tests. (mgorny)

•   (#470) Add note about updating DB using MySQL. (jugmac00)

•   (#468) Fix documentation - uia_phone_number should be uia_phone_mapper. (dvrg)

•   (#457) Improve chinese translations. (zxjlm)

•   (#453) Improve basque and spanish translations. (mmozos)

•   (#448) Add Afrikaans translations. (lonelyvikingmichael)

•   (#467) Add Blinker as explicit dependency, improve/fix celery usage docs, dont require pyqrcode unless authenticator configured, improve SMS configuration variables documentation.

  Version 4.0.0

Released January 26, 2021

PLEASE READ CHANGE NOTES CAREFULLY - THERE ARE LIKELY REQUIRED CHANGES YOU WILL HAVE TO MAKE TO EVEN START YOUR APPLICATION WITH 4.0

  Start Here

•   Your UserModel must contain fs_uniquifier

•   Either uninstall Flask-BabelEx (if you don't need translations) or add either Flask-Babel (>=2.0) or Flask-BabelEx to your dependencies AND be sure to initialize it in your app.

•   Add Flask-Mail to your dependencies.

•   If you have unicode emails or passwords read change notes below.

  Version 4.0.0rc2

Released January 18, 2021

  Features & Cleanup

•   Removal of python 2.7 and <3.6 support

•   Removal of token caching feature (a relatively new feature that had some systemic issues)

•   (#328) Remove dependence on Flask-Mail and refactor.

•   (#335) Remove two-factor /tf-confirm endpoint and use generic freshness mechanism.

•   (#336) Remove SECURITY_BACKWARDS_COMPAT_AUTH_TOKEN_INVALID(ATE). In addition to not making sense - the documentation has never been correct.

•   (#339) Require fs_uniquifier in the UserModel and stop using/referencing the UserModel primary key.

•   (#349) Change SECURITY_USER_IDENTITY_ATTRIBUTES configuration variable semantics.

•   Remove (all?) requirements around having an 'email' column in the UserModel. API change - JSON SPA redirects used to always include a query param 'email=xx'. While that is still sent (if and only if) the UserModel contains an 'email' columns, a new query param 'identity' is returned which returns the value of UserMixin.calc_username().

•   (#382) Improvements and documentation for two-factor authentication.

•   (#394) Add support for email validation and normalization (see MailUtil).

•   (#231) Normalize unicode passwords (see PasswordUtil).

•   (#391) Option to redirect to /confirm if user hits an endpoint that requires confirmation. New option SECURITY_REQUIRES_CONFIRMATION_ERROR_VIEW which if set and the user hits the /login, /reset, or /us-signin endpoint, and they require confirmation the response will be a redirect. (SnaKyEyeS)

•   (#366) Allow redirects on sub-domains. Please see SECURITY_REDIRECT_ALLOW_SUBDOMAINS. (willcroft)

•   (#376) Have POST redirects default to Flask's APPLICATION_ROOT. Previously the default configuration was /. Now it first looks at Flask's APPLICATION_ROOT configuration and uses that (which also by default is /. (tysonholub)

•   (#401) Add 2FA Validity Window so an application can configure how often the second factor has to be entered. (baurt)

•   (#403) Add HTML5 Email input types to email fields. This has some backwards compatibility concerns outlined below. (drola)

•   (#413) Add hy_AM translations. (rudolfamirjanyan)

•   (#410) Add Basque and fix Spanish translations. (mmozos)

•   (#408) Polish translations. (kamil559)

•   (#390) Update ru_RU translations. (TitaniumHocker)

  Fixed

•   (#389) Fixes for translations. First - email subjects were never being translated. Second, converted all templates to use _fsdomain(xx) rather than _(xx) so that they get translated regardless of the app's domain.

•   (#381) Support Flask-Babel 2.0 which has backported Domain support. Flask-Security now supports Flask-Babel (>=2.00), Flask-BabelEx, as well as no translation support. Please see backwards compatibility notes below.

•   (#352) Fix issue with adding/deleting permissions - all mutating methods must be at the datastore layer so that db.put() can be called. Added UserDatastore.add_permissions_to_role() and UserDatastore.remove_permissions_from_role(). The methods RoleMixin.add_permissions() and RoleMixin.remove_permissions() have been deprecated.

•   (#395) Provide ability to change table names for User and Role tables in the fsqla model.

•   (#338) All sessions are invalidated when a user changes or resets their password. This is accomplished by changing the user's fs_uniquifier. The user is automatically re-logged in (and a new session created) after a successful change operation.

•   (#418) Two-factor (and to a lesser extent unified sign in) QRcode fetching wasn't protected via CSRF. The fix makes things secure and simpler (always good); however read below for compatibility concerns. In addition, the elements that make up the QRcode (key, username, issuer) area also made available to the form and returned as part of the JSON return value - this allows for manual or other ways to initialize the authenticator app.

•   (#421) GET on /login and /change could return the callers authentication_token. This is a security concern since GETs don't have CSRF protection. This bug was introduced in 3.3.0.

  Backwards Compatibility Concerns

•   (#328) Remove dependence on Flask-Mail and refactor. The send_mail_task and send_mail methods as part of Flask-Security initialization have been removed and replaced with a new MailUtil class. The utility method send_mail() can still be used. If your application didn't use either of the deprecated methods, then the only change required is to add Flask-Mail to your package requirements (since Flask-Security no longer lists it). Please see the emails_topic for updated examples.

•   (#335) Convert two-factor setup flow to use the freshness feature rather than its own verify password endpoint. This COMPLETELY removes the /tf-confirm endpoint and associated form: two_factor_verify_password_form. Now, when /tf-setup is invoked, the flask_security.check_and_update_authn_fresh() is invoked, and if the current session isn't 'fresh' the caller will be redirected to a verify endpoint (either SECURITY_VERIFY_URL or SECURITY_US_VERIFY_URL). The simplest change would be to call /verify everywhere the application used to call /tf-confirm.

•   (#339) Require fs_uniquifier. In 3.3 the fs_uniquifier was added in the UserModel to fix the slow authentication token issue. In 3.4 the fs_uniquifier was used to implement Flask-Login's Alternative Token feature - thus decoupling the primary key (id) from any security context. All along, there have been a few issues with applications not wanting to use the name 'id' in their model, or wanting a different type for their primary key. With this change, Flask-Security no longer interprets or uses the UserModel primary key - just the fs_uniquifier field. See the changes section for 3.3 for information on how to do the schema and data upgrades required to add this field. There is also an API change - the JSON response (via UserModel.get_security_payload()) returned the user.id field. With this change the default is an empty directory - override UserMixin.get_security_payload() to return any portion of the UserModel you need.

•   (#349) SECURITY_USER_IDENTITY_ATTRIBUTES has changed syntax and semantics. It now contains the combined information from the old SECURITY_USER_IDENTITY_ATTRIBUTES and the newly introduced in 3.4 SECURITY_USER_IDENTITY_MAPPINGS. This enabled changing the underlying way we validate credentials in the login form and unified sign in form. In prior releases we simply tried to look up the form value as the PK of the UserModel - this often failed and then looped through the other SECURITY_USER_IDENTITY_ATTRIBUTES. This had a history of issues, including many applications not wanting to have a standard PK for the user model. Now, using the mapping configuration, the UserModel attribute/column the input corresponds to is determined, then the UserModel is queried specifically for that attribute:value pair. If you application didn't change the variable, no modifications are required.

•   (#354) The flask_security.PhoneUtil is now initialized as part of Flask-Security initialization rather than @app.before_first_request (since that broke the CLI). Since it isn't called in an application context, the app being initialized is passed as an argument to __init__.

•   (#381) When using Flask-Babel (>= 2.0) it is required that the application initialize Flask-Babel (e.g. Babel(app)). Flask-BabelEx would self-initialize so it didn't matter. Flask-Security will throw a run time error upon first request if Flask-Babel OR FLask-BabelEx is installed, but not initialized. Also, Flask-Security no longer has a dependency on either Flask-Babel or Flask-BabelEx - if neither are installed, it falls back to a dummy translation. If your application expects translation services, it must specify the appropriate dependency AND initialize it.

•   (#394) Email input is now normalized prior to being stored in the DB. Previously, it was validated, but the raw input was stored. Normalization and validation rely on the email_validator package. The MailUtil class provides the interface for normalization and validation - allowing all this to be customized. If you have unicode local or domain parts - existing users may have difficulties logging in. Administratively you need to read each user record, normalize the email (see MailUtil), and write it back.

•   (#381) Passwords are now, by default, normalized using Python's unicodedata.normalize() method. The SECURITY_PASSWORD_NORMALIZE_FORM defaults to "NKFD". This brings Flask-Security in line with the NIST recommendations outlined in Memorized Secret Verifiers If your users have unicode passwords they may have difficulty authenticating. You can turn off this normalization or have your users reset their passwords. Password normalization and validation has been encapsulated in a new PasswordUtil class. This replaces the method password_validator introduced in 3.4.0.

•   (#403) By default all forms that have an email as input now use the wtforms html5 EmailField. For most applications this will make the user experience slightly nicer - especially for mobile devices. Some applications use the email form field for other identity attributes (such as username). If your application does this you will probably need to subclass LoginForm and change the email type back to StringField.

•   (#338) By default, both passwords and authentication tokens use the same attribute fs_uniquifier to uniquely identify the user. This means that if the user changes or resets their password, all authentication tokens also become invalid. This could be viewed as a feature or a bug. If this behavior isn't desired, add another uniquifier: fs_token_uniquifier to your UserModel and that will be used to generate authentication tokens.

•   (#418) Fix CSRF vulnerability w.r.t. getting QRcodes. Both two-factor and unified-signup had a separate GET endpoint to fetch the QRcode when setting up an authenticator app. GETS don't have any CSRF protection. Both of those endpoints have been completely removed, and the QRcode is embedded in a successful POST of the setup form. The changes to the templates are minimal and of course if you didn't override the template - there is no compatibility concern.

•   (#421) Fix CSRF vulnerability on /login and /change that could return the callers authentication token. Now, callers can only get the authentication token on successful POST calls.

  Version 3.4.5

Released January 8, 2021

Security Vulnerability Fix.

Two CSRF vulnerabilities were reported: qrcode and login. This release fixes the more severe of the 2 - the /login vulnerability. The QRcode issue has a much smaller risk profile since a) it is only for two-factor authentication using an authenticator app b) the qrcode is only available during the time the user is first setting up their authentication app. The QRcode issue has been fixed in 4.0.

  Fixed

•   (#421) GET on /login and /change could return the callers authentication_token. This is a security concern since GETs don't have CSRF protection. This bug was introduced in 3.3.0.

  Backwards Compatibility Concerns

•   (#421) Fix CSRF vulnerability on /login and /change that could return the callers authentication token. Now, callers can only get the authentication token on successful POST calls.

  Version 3.4.4

Released July 27, 2020

Bug/regression fixes.

  Fixed

•   (#359) Basic Auth broken. When the unauthenticated handler was changed to provide a more uniform/consistent response - it broke using Basic Auth from a browser, since it always redirected rather than returning 401. Now, if the response headers contain WWW-Authenticate (which is set if basic @auth_required method is used), a 401 is returned. See below for backwards compatibility concerns.

•   (#362) As part of figuring out issue 359 - a redirect loop was found. In release 3.3.0 code was put in to redirect to SECURITY_POST_LOGIN_VIEW when GET or POST was called and the caller was already authenticated. The method used would honor the request next query parameter. This could cause redirect loops. The pre-3.3.0 behavior of redirecting to SECURITY_POST_LOGIN_VIEW and ignoring the next parameter has been restored.

•   (#347) Fix peewee. Turns out - due to lack of unit tests - peewee hasn't worked since 'permissions' were added in 3.3. Furthermore, changes in 3.4 around get_id and alternative tokens also didn't work since peewee defines its own get_id method.

  Compatibility Concerns

In 3.3.0, flask_security.auth_required() was changed to add a default argument if none was given. The default include all current methods - session, token, and basic. However basic really isn't like the others and requires that we send back a WWW-Authenticate header if authentication fails (and return a 401 and not redirect). basic has been removed from the default set and must once again be explicitly requested.

  Version 3.4.3

Released June 12, 2020

Minor fixes for a regression and a couple other minor changes

  Fixed

•   (#340) Fix regression where tf_phone_number was required, even if SMS wasn't configured.

•   (#342) Pick up some small documentation fixes from 4.0.0.

  Version 3.4.2

Released May 2, 2020

Only change is to move repo to the Flask-Middleware github organization.

  Version 3.4.1

Released April 22, 2020

Fix a bunch of bugs in new unified sign in along with a couple other major issues.

  Fixed

•   (#298) Alternative ID feature ran afoul of postgres/psycopg2 finickiness.

•   (#300) JSON 401 responses had WWW-Authenticate Header attached - that caused browsers to pop up their own login/password form. Not what applications want.

•   (#280) Allow admin/api to setup TFA (and unified sign in) out of band. Please see UserDatastore.tf_set(), UserDatastore.tf_reset(), UserDatastore.us_set(), UserDatastore.us_reset() and UserDatastore.reset_user_access().

•   (#305) We used form._errors which wasn't very pythonic, and it was removed in WTForms 2.3.0.

•   (#310) WTForms 2.3.0 made email_validator optional - we need it.

  Version 3.4.0

Released March 31, 2020

  Features

•   (#257) Support a unified sign in feature. Please see unified-sign-in.

•   (#265) Add phone number validation class. This is used in both unified sign in as well as two-factor when using sms.

•   (#274) Add support for 'freshness' of caller's authentication. This permits endpoints to be additionally protected by ensuring a recent authentication.

•   (#99, #195) Support pluggable password validators. Provide a default validator that offers complexity and breached support.

•   (#266) Provide interface to two-factor send_token so that applications can provide error mitigation. Defaults to returning errors if can't send the verification code.

•   (#247) Updated all-inclusive data models (fsqlaV2). Add fields necessary for the new unified sign in feature and changed 'username' to be unique (but not required).

•   (#245) Use fs_uniquifier as the default Flask-Login 'alternative token'. Basically this means that changing the fs_uniquifier will cause outstanding auth tokens, session and remember me cookies to be invalidated. So if an account gets compromised, an admin can easily stop access. Prior to this cookies were storing the 'id' which is the user's primary key - difficult to change! (kishi85)

  Fixed

•   (#273) Don't allow reset password for accounts that are disabled.

•   (#282) Add configuration that disallows GET for logout. Allowing GET can cause some denial of service issues. The default still allows GET for backwards compatibility. (kantorii)

•   (#258) Reset password wasn't integrated into the two-factor feature and therefore two-factor auth could be bypassed.

•   (#254) Allow lists and sets as underlying permissions. (pffs)

•   (#251) Allow a registration form to have additional fields that aren't part of the user model that are just passed to the user_registered.send signal, where the application can perform arbitrary additional actions required during registration. (kuba-lilz)

•   (#249) Add configuration to disable the 'role-joining' optimization for SQLAlchemy. (pffs)

•   (#238) Fix more issues with atomically setting the new TOTP secret when setting up two-factor. (kishi85)

•   (#240) Fix Quart Compatibility. (ristellise)

•   (#232) CSRF Cookie not being set when using 'Remember Me' cookie to re-sign in. (kishi85)

•   (#229) Two-factor enabled accounts didn't work with the Remember Me feature. (kishi85)

    As part of adding unified sign in, there were many similarities with two-factor. Some refactoring was done to unify naming, configuration variables etc. It should all be backwards compatible.

•   In TWO_FACTOR_ENABLED_METHODS "mail" was changed to "email". "mail" will still be honored if already stored in DB. Also "google_authenticator" is now just "authenticator".

•   TWO_FACTOR_SECRET, TWO_FACTOR_URI_SERVICE_NAME, TWO_FACTOR_SMS_SERVICE, and TWO_FACTOR_SMS_SERVICE_CONFIG have all been deprecated in favor of names that are the same for two-factor and unified sign in.

    Other changes with possible backwards compatibility issues:

•   /tf-setup never did any phone number validation. Now it does.

•   two_factor_setup.html template - the chosen_method check was changed to email. If you have your own custom template - be sure make that change.

  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) Unauthorized 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)
# for MySQL the previous line has to be replaced with...
# op.alter_column('user', 'fs_uniquifier', existing_type=sa.String(length=64), nullable=False)

  Version 3.2.0

Released June 26th 2019

•   (#80) Support caching of authentication token (eregnier opr #839). 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

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

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

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

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

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

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

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

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

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

•   (#64) Customizable send_mail (abulte opr #730)

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

    These should all be backwards compatible.

    Possible compatibility issues:

•   #487 - prior to this, render_template() was overridable 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 solvable 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 respectively 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 obsolete 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 insensitivity

•   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 configuration 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 accommodate 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-2021