You are on page 1of 102

Flask-User Documentation

Release v1.0

Ling Thio

Sep 16, 2017


Contents

1 Customizable, yet Ready to use 3

2 Well documented 5

3 Additional features 7

4 Minimal Requirements 9

5 Alternatives 11

6 Authors 13

7 Table of Contents 15
7.1 Flask-User v0.9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
7.2 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
7.3 QuickStart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
7.4 Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
7.5 Data-models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7.6 Role-based Authorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
7.7 Customizing Flask-User . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
7.8 Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
7.9 Porting v0.6 to v9.0+ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
7.10 Change history . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
7.11 Flask-User API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
7.12 QuickStart App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
7.13 Basic App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
7.14 MongoDB App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
7.15 DbAdapter Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
7.16 View decorators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
7.17 EmailAdapter Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
7.18 Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
7.19 UserManager Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
7.20 UserManager Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
7.21 UserManager Utility methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
7.22 UserManager View methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
7.23 Configuring settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
7.24 Customizing Emails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

i
7.25 Customizing Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
7.26 Advanced Customizations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
7.27 Porting Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
7.28 Porting Customizations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
7.29 Advanced Porting topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
7.30 Base templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
7.31 Signals (event hooking) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
7.32 Recipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
7.33 F.A.Q. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
7.34 Flask-User API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
7.35 Misc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
7.36 Customize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

ii
Flask-User Documentation, Release v1.0

Customizable User Authentication, User Management, and more.

Attention

Flask-User v0.9 is in its Alpha stage (under active development).


The latest stable version is Flask-User v0.6.

So, you’re writing a Flask web application and would like to authenticate your users.
You start with a simple Login page, but soon enough you’ll need to handle:

• Registrations and Email Confirmations


• Change Usernames, Change Passwords, and Forgotten Passwords
And wouldn’t it be nice to also offer:
• Added Security
• Increased Reliability
• Role-based Authorization
• Internationalization (Chinese, Dutch, English, Farsi, Finnish, French, German, Italian, Russian, Spanish,
Swedish, and Turkish)

Contents 1
Flask-User Documentation, Release v1.0

2 Contents
CHAPTER 1

Customizable, yet Ready to use

• Largely Configurable – By overriding configuration settings.


• Fully Customizable – By overriding methods and properties.
• Ready to use – Through sensible defaults.
• Supports SQL and MongoDB databases.

3
Flask-User Documentation, Release v1.0

4 Chapter 1. Customizable, yet Ready to use


CHAPTER 2

Well documented

• Latest documentation
• Flask-User v0.6 documentation
• Flask-User v0.5 documentation

5
Flask-User Documentation, Release v1.0

6 Chapter 2. Well documented


CHAPTER 3

Additional features

• MIT License
• Tested on Python 2.6, 2.7, 3.3, 3.4, 3.5 and 3.6. Coverage: Over 90%.
• Event hooking – Through efficient signals.
• Support for multiple emails per user

7
Flask-User Documentation, Release v1.0

8 Chapter 3. Additional features


CHAPTER 4

Minimal Requirements

• brypt 2.0+
• cryptography 1.6+
• Flask 0.9+
• Flask-Login 0.2+
• Flask-WTF 0.9+
• passlib 1.6+

9
Flask-User Documentation, Release v1.0

10 Chapter 4. Minimal Requirements


CHAPTER 5

Alternatives

• Flask-Login
• Flask-Security

11
Flask-User Documentation, Release v1.0

12 Chapter 5. Alternatives
CHAPTER 6

Authors

Lead developer and Maintainer


Ling Thio – ling.thio AT gmail DOT com

Contributors
Many contributors

13
Flask-User Documentation, Release v1.0

14 Chapter 6. Authors
CHAPTER 7

Table of Contents

7.1 Flask-User v0.9

Customizable User Authentication, User Management, and more.

Attention

Flask-User v0.9 is in its Alpha stage (under active development).


The latest stable version is Flask-User v0.6.

So, you’re writing a Flask web application and would like to authenticate your users.
You start with a simple Login page, but soon enough you’ll need to handle:

• Registrations and Email Confirmations


• Change Usernames, Change Passwords, and Forgotten Passwords
And wouldn’t it be nice to also offer:
• Added Security
• Increased Reliability
• Role-based Authorization
• Internationalization (Chinese, Dutch, English, Farsi, Finnish, French, German, Italian, Russian, Spanish,
Swedish, and Turkish)

15
Flask-User Documentation, Release v1.0

7.1.1 Customizable, yet Ready to use

• Largely Configurable – By overriding configuration settings.


• Fully Customizable – By overriding methods and properties.
• Ready to use – Through sensible defaults.
• Supports SQL and MongoDB databases.

7.1.2 Well documented

• Latest documentation
• Flask-User v0.6 documentation
• Flask-User v0.5 documentation

7.1.3 Additional features

• MIT License
• Tested on Python 2.6, 2.7, 3.3, 3.4, 3.5 and 3.6. Coverage: Over 90%.
• Event hooking – Through efficient signals.
• Support for multiple emails per user

7.1.4 Minimal Requirements

• brypt 2.0+
• cryptography 1.6+
• Flask 0.9+
• Flask-Login 0.2+
• Flask-WTF 0.9+
• passlib 1.6+

7.1.5 Alternatives

• Flask-Login
• Flask-Security

7.1.6 Authors

Lead developer and Maintainer


Ling Thio – ling.thio AT gmail DOT com

Contributors
Many contributors

16 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

7.2 Installation

We recommend making use of virtualenv, virtualenvwrapper and pip:

# Create virtual enviroment


mkvirtualenv my_app

# Switch to virtual environment


workon my_app

# Install Flask-User
pip install Flask-User

7.2.1 Optional uninstalls

• FLask-User installs bcrypt for its default passlib hash of ‘bcrypt’.


• Flask-User installs Flask-Mail for its default SMTPEmailAdapter.
• Flask-User installs Flask-SQLAlchemy for its default SQLDbAdapter.
If you configure/customize Flask-User away from their defaults, certain packages may be uninstalled:

# Optionally uninstall unused packages


pip uninstall bcrypt
pip uninstall Flask-Mail
pip uninstall Flask-SQLAlchemy

7.3 QuickStart

Intro QuickStart App Basic App MongoDB App

With less than a dozen lines of code, we can extend existing Flask applications with the following additional function-
ality:
• User registration with username and/or email
• Email confirmation
• User authentication (Login and Logout)
• Change username
• Change password
• Forgot password

7.3.1 Choose a QuickStart app

• QuickStart App – Login with username. No need to configure SMTP.


• Basic App – Login with email, Role-based authentication, and Translations.
• MongoDB App – QuickStart App for MongoDB.

7.2. Installation 17
Flask-User Documentation, Release v1.0

7.3.2 Flask-User-starter-app

While the example apps provide a quick way to illustrate the use of Flask-User, we do not recommend its single-file
techniques.
The Flask-User-starter-app follows typical Flask application practices using multiple files in an organized directory
structure:
app/
commands/
models/
static/
templates/
views/
tests/

See https://github.com/lingthio/Flask-User-starter-app
This may serve as a great starting place to create your next Flask application.

Intro QuickStart App Basic App MongoDB App

7.4 Limitations

We want to be transparent about what this package can and can not do.

7.4.1 Python versions

Flask-User has been tested with Python 2.6, 2.7, 3.3, 3.4, 3.5 and 3.6.

7.4.2 Flask versions

Flask-User works with Flask 0.9+

7.4.3 Supported Databases

Flask-User makes use of DbAdapters to support different databases.


It ships with a SQLDbAdapter to support a wide range of SQL databases via Flask-SQLAlchemy (Firebird, Microsoft
SQL Server, MySQL, Oracle, PostgreSQL, SQLite, Sybase and more).
It ships with a MongoDbAdapter to support MongoDB databases via Flask-MongoEngine.
Custom DbAdapters can be implemented to support other Databases.

7.4.4 Supported Email Mailers

Flask-User makes use of EmailAdapters to send email via several platforms.


It ships with a SMTPEmailAdapter a SendmailEmailAdapter and a SendGridEmailAdapter to send emails via SMTP,
sendmail and SendGrid.
Custom EmailAdapters can be implemented to support other Email Mailers.

18 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

7.4.5 Fixed app.user_manager name

An initialized UserManager() instance will assign itself to the app.user_manager property. This app.
user_manager name can not be changed.

7.4.6 Fixed data-model property names

The following data-model property names are fixed:

User.id
User.password
User.username # optional
User.email # optional
User.email_confirmed_at # optional
User.active # optional
User.roles # optional
User.user_emails # optional
Role.name # optional
UserEmail.id # optional
UserEmail.email # optional
UserEmail.email_confirmed_at # optional
UserInvitation.id # optional
UserInvitation.email # optional
UserInvitation.invited_by_user_id # optional

If you have existing code, and are unable to globally change a fixed property name, consider using Python’s getter and
setter properties as a bridge:

class User(db.Model, UserMixin):


...
# Existing code uses email_address instead of email
email_address = db.Column(db.String(255), nullable=False, unique=True)
...

# define email getter


@property
def email(self):
return self.email_address # on user.email: return user.email_address

# define email setter


@email.setter
def email(self, value):
self.email_address = value # on user.email='xyz': set user.email_address='xyz
˓→ '

7.4.7 Flexible data-model class, SQL table, and SQL column names

Data-model class names are unrestricted.


SQL table names are unrestricted.
SQL column names are unrestricted.

Here is an example of a data-model class with different class, table and column names:

7.4. Limitations 19
Flask-User Documentation, Release v1.0

# Use of the Member class name (instead of User)


class Member(db.Model, UserMixin):

# Use of the 'members' SQL table (instead of 'users')


__tablename__ = 'members'
...
# Use of the 'email_address' SQL column (instead of 'email')
email = db.Column('email_address', db.String(255), nullable=False, unique=True)

# Setup Flask-User
user_manager = UserManager(app, db, Member) # Specify the Member class

7.4.8 Primary keys

Even though Flask-User relies on the following:


• Primary key is a single property named id.
• id properties are:
– integers,
– or strings,
– or offer a string representation with str(id).
Developers can still support primary key properties named other than id:

class User(db.Model, UserMixin):


# Composite primary key
pk = db.Column(db.Integer, primary_key=True)
...
# Map: id=user.id to: id=user.pk
@property
def id(self):
return self.pk

# Map: user.id=id to: user.pk=id


@id.setter
def id(self, value):
self.pk = value

Developers can still support composite primary keys:

class User(db.Model, UserMixin):


# Composite primary key
pk1 = db.Column(db.Integer, primary_key=True)
pk2 = db.Column(db.String, primary_key=True)
...
# Map: id=user.id to: id=str(pk1)+'|'+pk2
@property
def id(self):
return str(self.pk1)+'|'+self.pk2 # Naive concatenation

# Map: user.id=str(pk1)+'|'+pk2 to: user.pk1=pk1; user.pk2=pk2;


@id.setter
def id(self, value):
items = value.split('|',1) # Naive split

20 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

self.pk1 = int(items[0])
self.pk2 = items[1]

Developers can customize the TokenManager to accept IDs without string representations.

7.5 Data-models

Note: The code examples below assume the use of Flask-SQLAlchemy

7.5.1 User data-model

In its simplest form, Flask-User makes use of a single data-model class called User:

# Define User data-model


class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)

# User Authentication fields


email = db.Column(db.String(255), nullable=False, unique=True)
email_confirmed_at = db.Column(db.DateTime())
username = db.Column(db.String(50), nullable=False, unique=True)
password = db.Column(db.String(255), nullable=False)

# User fields
active = db.Column(db.Boolean()),
first_name = db.Column(db.String(50), nullable=False)
last_name = db.Column(db.String(50), nullable=False)

# Setup Flask-User
user_manager = UserManager(app, db, User)

The active property is optional. Add it if your application needs to disable users. Flask-User will not let users login
if this field is set to False.

7.5.2 Flexible class name

The User class name can be anything you want:

class Client(db.Model, UserMixin):


...

user_manager = UserManager(app, db, Client)

7.5.3 Fixed data-model property names

The following data-model property names are fixed:

User.id
User.username # optional
User.password

7.5. Data-models 21
Flask-User Documentation, Release v1.0

User.email # optional
User.email_confirmed_at # optional
User.active # optional
User.roles # optional
User.user_emails # optional
Role.id # optional
Role.name # optional

The following property names are flexible:: UserRoles.id # optional UserRoles.user_id # optional User-
Roles.role_id # optional
If you have existing code, and are unable to globally change the fixed property names, consider using helper getters
and setters as a bridge:

class User(db.Model, UserMixin):


...
# Existing code uses email_address instead of email
email_address = db.Column(db.String(255), nullable=False, unique=True)
...

# define email getter


@property
def email(self):
return self.email_address # on user.email: return user.email_address

# define email setter


@email.setter
def email(self, value):
self.email_address = value # on user.email='xyz': set user.email_address='xyz
˓→ '

7.5.4 Flexible database column names

SQLAlchemy allows developers to specify a database column name different from their corresponding data-model
property name like so:

class User(db.Model, UserMixin):


...
# Map email property to email_address column
email = db.Column('email_address', db.String(255), nullable=False, unique=True)

7.5.5 Optional Role and UserRoles data-models

The optional Role and UserRoles data-models are only required for role-based authentication. In this configura-
tion, the User data-model must aslo define a roles relationship property.
The Role data-model holds the name of each role. This name will be matched to the @roles_required function
decorator in a CASE SENSITIVE manner.
The UserRoles data-model associates Users with their Roles.

# Define the User data-model


class User(db.Model, UserMixin):
...
# Relationships

22 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

roles = db.relationship('Role', secondary='user_roles')

# Define the Role data-model


class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(50), unique=True)

# Define the UserRoles association table


class UserRoles(db.Model):
__tablename__ = 'user_roles'
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.Integer(), db.ForeignKey('users.id', ondelete='CASCADE'))
role_id = db.Column(db.Integer(), db.ForeignKey('roles.id', ondelete='CASCADE'))

Roles are defined by adding rows to the Role table with a specific Role.name value.

admin_role = Role(name='Admin')
db.session.commit()

Users are assigned one or more roles by adding them to the User.roles property:

# Create 'user007' user with 'secret' and 'agent' roles


user1 = User(
username='user007', email='admin@example.com', active=True,
password=user_manager.hash_password('Password1'))
user1.roles = [admin_role,]
db.session.commit()

7.5.6 Optional UserEmail data-model

Flask-User can be configured to allow for multiple emails per users, pointing to the same user account and sharing the
same password. In this configuration, a separate UserEmail data-model class must be specified.

# Define User data-model


class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)

# User Authentication fields


username = db.Column(db.String(50), nullable=False, unique=True)
password = db.Column(db.String(255), nullable=False)

# Relationship
user_emails = db.relationship('UserEmail')

# Define UserEmail data-model


class UserEmail(db.Model):
__tablename__ = 'user_emails'
id = db.Column(db.Integer, primary_key=True)

user_id = db.Column(db.Integer, db.ForeignKey('users.id'))


user = db.relationship('User', uselist=False)

# User email information

7.5. Data-models 23
Flask-User Documentation, Release v1.0

email = db.Column(db.String(255), nullable=False, unique=True)


email_confirmed_at = db.Column(db.DateTime())
is_primary = db.Column(db.Boolean(), nullable=False, server_default='0')

# Setup Flask-User
user_manager = UserManager(app, User, UserEmailClass=UserEmail)

The is_primary property defines which email receives account notification emails.

7.5.7 Optional UserEmail data-model

[TBD]

7.6 Role-based Authorization

Authorization is the process of specifying and enforcing access rights of users to resources.
Flask-User offers role-based authorization through the use of the @roles_required decorator.

7.6.1 @roles_required

If a view function is decorated with the @roles_required decorator, the user:


• must be logged in, and
• must be associated with the specified role names.
If any of these conditions is not met, an ‘Unauthorized access’ error message will be shown and the user will be
redirected to the USER_UNAUTHORIZED_ENDPOINT.
In the example below the current user is required to be logged in and to be associated with the role named ‘Admin’:

from flask_user import roles_required

@route('/admin/dashboard') # @route() must always be the outer-most decorator


@roles_required('Admin')
def admin_dashboard():
# render the admin dashboard

Note: Comparison of role names is case sensitive, so 'Admin' will NOT match 'admin'.

Simple AND/OR operations

The @roles_required decorator accepts one or more role names. At the decorator level, if multiple role names are
specified here, the user must have all the specified roles. This is the AND operation.
At the argument level, each item may be a role name or a list or role names. If a list of role names is specified here,
the user mast have any one of the specified roles to gain access. This is the OR operation.
In the example below, the user must always have the 'Starving' role, AND either the 'Artist' role OR the
'Programmer' role:

24 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

# Ensures that the user is ('Starving' AND (an 'Artist' OR a 'Programmer'))


@roles_required('Starving', ['Artist', 'Programmer'])

Note: The nesting level only goes as deep as this example shows.

7.6.2 Required Role and UserRoles data-models

The @roles_required decorator depends the Role and UserRoles data-models (in addition to the User data-
model).
See the docs on Role and UserRoles data-models.

7.6.3 Example App

The Basic App demonstrates the use of the @roles_required decorator.

7.7 Customizing Flask-User

Intro Settings Forms Emails Advanced

From the very beginning, Flask-User was specifically designed to be fully customizable in addition to being secure
and reliable.
• Flask-User can be configured through Configuring settings.
– Enabling and disabling of features
– General settings
– Token expiration settings
– Complete control of Passlib’s password hash settings
– Form URLs
– Form template file paths
– Email template file paths
• Customizing Forms
– Form Templates
– Customizing Form Classes
– Customizing Form Validators
– Customizing Form Views
• Customizing Emails
– Customizing Email templates
• And then there are Advanced Customizations
– Customizing the Email, Password and Token Managers
– Implementing a CustomDbAdapter

7.7. Customizing Flask-User 25


Flask-User Documentation, Release v1.0

– Implementing a CustomEmailAdapter

Intro Settings Forms Emails Advanced

7.8 Internationalization

Flask-User ships with the following language translations:


• Chinese Simplified (zh)
• Dutch (nl)
• English (en)
• German (de)
• Farsi (fa)
• Finnish (fi)
• French (fr)
• Italian (it)
• Russian (ru)
• Spanish (es)
• Swedish (sv)
• Turkish (tr)
They can be found in the Flask-User installation directory, under the translations subdirectory. Each translation
file is called flask_user.mo (called a domain translation) to differentiate from your application’s translations,
typically called messages.mo

7.8.1 REQUIRED: Installing Flask-BabelEx

There are two distinct Flask extensions for managing translations: Flask-Babel and Flask-BabelEx.
Flask-User relies on the domain-specific abilities of Flask-BabelEx and will not translate with Flask-Babel:

# Uninstall Flask-Babel if needed


pip uninstall Flask-Babel

# Install Flask-BabelEx
pip intall Flask-BabelEx

7.8.2 REQUIRED: Initializing Flask-BabelEx

Flask-BabelEx must be initialized:


• After Flask-User initialization
• After the app config has been loaded
• Before Flask-User initialization

26 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

Example:

from flask import Flask, request


from flask_babelex import Babel
from flask_user import UserManager
...
# Setup Flask
app = Flask(__name__)
...
# Read applicaton config
app.config.from_object('app.config.settings')
...
# Initialize Flask-BabelEx
babel = Babel(app)
...
# Initialize Flask-User
user_manager = UserManager(app, db, User)

7.8.3 REQUIRED: Setting your browser language preference

You will need to add and prioritize one of the Flask-User supported languages in your browser.
For Google Chrome:
• Chrome > Preferences. Search for ‘Language’.
• Expand the ‘Language’ bar > Add languages.
• Check the ‘Dutch’ checkbox > Add.
• Make sure to move it to the top: Three dots > Move to top.
You can test this with the Basic App.

7.9 Porting v0.6 to v9.0+

Porting Basics Customizations Advanced

Ever since Flask-User v0.4, we had plans to improve Flask-User but were held back by our desire to maintain back-
wards compatibility for a while.
With Flask-User v1.0 (and its v0.9 alpha/beta) we decided it was time to move forward, breaking compatibility with
v0.6.

7.9.1 Porting slightly customized v0.6 applications

If you’ve only customized Flask-User v0.6 in the following ways:


• Changed USER_... app config settings
• Customized form templates (the .html files)
• Customized email templates (the .html or .txt files)
Reading Porting Basics will suffice.

7.9. Porting v0.6 to v9.0+ 27


Flask-User Documentation, Release v1.0

7.9.2 Porting highly customized v0.6 applications

If you have:
• Specified custom form classes (the .py files)
• Specified custom view functions
• Specified custom password or username validators
• Specified a custom TokenManager
• Used the optional UserAuth class
you will also need to read: Porting Customizations.

7.9.3 Porting Tips

See Advanced Porting topics

Porting Basics Customizations Advanced

7.10 Change history

With v0.9 (alpha and beta) and v1.0 (stable) we simplified customization by allowing developers to override or
extend UserManager properties and methods.
We increased security by having the TokenManager accept parts of passwords, in addition to the user ID, to invalidate
tokens after a password has changed. The TokenManager now also excepts IDs other than small integers.
Hashlib password hashing is completely configurable through two config settings:
USER_PASSLIB_CRYPTCONTEXT_SCHEMES and USER_PASSLIB_CRYPTCONTEXT_KEYWORDS.
Example: SCHEMES=['bcrypt', 'argon2'], KEYWORDS=dict(bcrypt__rounds=12,
argon2__memory_cost=512).
We added support for MongoDBs (through Flask-MongoEngine) and for DynamoDBs (through Flask-Flywheel).
We introduced the EmailAdapter interface to support sending emails not only via SMTP, but also via sendmail,
SendGrid, and custom EmailAdapters.
For all of the above we finally had to break compatibility with v0.6 (stable). For non-customized Flask-User apps, the
porting is relatively straightforward. See the ‘Porting from v0.6 to v0.9+’ section in our docs.
• v0.9.0 - First 1.0 alpha release
• v0.6.* - No longer compatible

7.11 Flask-User API

• UserManager Class
• UserManager Settings
• UserManager Utility methods
• UserManager View methods

28 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

• View decorators
• Forms
• DBManager class
• EmailManager class
• PasswordManager class
• TokenManager class
• DbAdapter Interface
• EmailAdapter Interface

7.11.1 DBManager class

This class manages database objects.


class DBManager(app, db, UserClass, UserEmailClass=None, UserInvitationClass=None, Role-
Class=None)
Manage DB objects.
Initialize the appropriate DbAdapter, based on the db parameter type.
Parameters
• app (Flask) – The Flask application instance.
• db – The Object-Database Mapper instance.
• UserClass – The User class.
• UserEmailClass – Optional UserEmail class for multiple-emails-per-user feature.
• UserInvitationClass – Optional UserInvitation class for user-invitation feature.
• RoleClass – For testing purposes only.
add_user_role(user, role_name)
Associate a role name with a user.
add_user(**kwargs)
Add a User object, with properties specified in **kwargs.
add_user_email(user, **kwargs)
Add a UserEmail object, with properties specified in **kwargs.
add_user_invitation(**kwargs)
Add a UserInvitation object, with properties specified in **kwargs.
commit()
Commit session-based objects to the database.
delete_object(object)
Delete and object.
find_user_by_username(username)
Find a User object by username.
find_user_emails(user)
Find all the UserEmail object belonging to a user.

7.11. Flask-User API 29


Flask-User Documentation, Release v1.0

get_primary_user_email(user)
Retrieve the email from User object or the primary UserEmail object (if multiple emails per user are
enabled).
get_user_and_user_email_by_id(user_or_user_email_id)
Retrieve the User and UserEmail object by ID.
get_user_and_user_email_by_email(email)
Retrieve the User and UserEmail object by email address.
get_user_by_id(id)
Retrieve a User object by ID.
get_user_email_by_id(id)
Retrieve a UserEmail object by ID.
get_user_invitation_by_id(id)
Retrieve a UserInvitation object by ID.
get_user_roles(user)
Retrieve a list of user role names.

Note: Database management methods.

save_object(object)
Save an object to the database.
save_user_and_user_email(user, user_email)
Save the User and UserEmail object.
user_has_confirmed_email(user)

Return True if user has a confirmed email.


Return False otherwise.
username_is_available(new_username)
Check if new_username is still available.

Returns True if new_username does not exist or belongs to the current user.
Return False otherwise.

create_all_tables()
Create database tables for all known database data-models.
drop_all_tables()
Drop all tables.

Warning: ALL DATA WILL BE LOST. Use only for automated testing.

30 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

7.11.2 EmailManager class

This class manages the sending of Flask-User emails.


class EmailManager(app)
Send emails via the configured EmailAdapter user_manager.email_adapter.
Parameters app (Flask) – The Flask application instance.
send_confirm_email_email(user, user_email)
Send the ‘email confirmation’ email.
send_password_changed_email(user)
Send the ‘password has changed’ notification email.
send_reset_password_email(user, user_email)
Send the ‘reset password’ email.
send_invite_user_email(user, user_invitation)
Send the ‘user invitation’ email.
send_registered_email(user, user_email, request_email_confirmation)
Send the ‘user has registered’ notification email.
send_username_changed_email(user)
Send the ‘username has changed’ notification email.
See also:
Customizing the EmailManager.

7.11.3 PasswordManager class

The PasswordManager generates and verifies hashed passwords.


class PasswordManager(app)
Hash and verify user passwords using passlib
Create a passlib CryptContext.
Parameters password_hash (str) – The name of a valid passlib password hash. Examples:
'bcrypt', 'pbkdf2_sha512', 'sha512_crypt' or 'argon2'.

Example

password_manager = PasswordManager('bcrypt')
hash_password(password)
Hash plaintext password using the password_hash specified in the constructor.
Parameters password (str) – Plaintext password that the user types in.
Returns hashed password.

7.11. Flask-User API 31


Flask-User Documentation, Release v1.0

Example

user.password = hash_password('mypassword')
verify_password(password, password_hash)
Verify plaintext password against hashed password.
Parameters
• password (str) – Plaintext password that the user types in.
• password_hash (str) – Password hash generated by a previous call to
hash_password().
Returns

True when password matches password_hash.


False otherwise.

Example

if verify_password('mypassword', user.password):
login_user(user)

See also:
Customizing the PasswordManager.

7.11.4 TokenManager class

The TokenManager generates and verifies timestamped, signed and encrypted tokens.
These tokens are used in the following places:
• To securely store User IDs in the browser session cookie.
• To provide secure tokens in email-confirmation emails.
• To provide secure tokens in reset-password emails.
class TokenManager(app)
Generate and verify timestamped, signed and encrypted tokens.
Check config settings and initialize the Fernet encryption cypher.
Fernet is basically AES128 in CBC mode, with a timestamp and a signature.
Parameters app (Flask) – The Flask application instance.
generate_token(*args)
Convert a list of integers or strings, specified by *args, into an encrypted, timestamped, and signed token.
Note: strings may not contain any '|' characters, nor start with a '~' character as these are used as
separators and integer indicators for encoding.
Example:

32 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

# Combine User ID with last 8 bytes of their password


# to invalidate tokens when passwords change.
user_id = user.id
password_ends_with = user.password[-8:0]
token = token_manager.generate_token(user_id, password_ends_with)

verify_token(token, expiration_in_seconds)
Verify token signature, verify token expiration, and decrypt token.

Returns None if token is expired or invalid.


Returns a list of strings and integers on success.

Implemented as:

concatenated_str = self.decrypt_string(token, expiration_in_seconds)


data_items = self.decode_data_items(concatenated_str)
return data_items

Example:

# Verify that a User with ``user_id`` has a password that ends in ``password_
˓→ends_with``.

token_is_valid = False
data_items = token_manager.verify(token, expiration_in_seconds)
if data_items:
user_id = data_items[0]
password_ends_with = data_items[1]
user = user_manager.db_manager.get_user_by_id(user_id)
token_is_valid = user and user.password[-8:]==password_ends_with

encrypt_string(concatenated_str)
Timestamp, sign and encrypt a string into a token using cryptography.fernet.Fernet().
decrypt_string(token_str, expiration_in_seconds)
Verify signature, verify timestamp, and decrypt a token using cryptography.fernet.Fernet().
encode_data_items(*args)
Encodes a list of integers and strings into a concatenated string.
• encode string items as-is.
• encode integer items as base-64 with a '~' prefix.
• concatenate encoded items with a '|' separator.

Example

encode_data_items('abc', 123, 'xyz') returns 'abc|~B7|xyz'


decode_data_items(concatenated_str)
Decodes a concatenated string into a list of integers and strings.

7.11. Flask-User API 33


Flask-User Documentation, Release v1.0

Example

decode_data_items('abc|~B7|xyz') returns ['abc', 123, 'xyz']


encode_int(n)
Encodes an integer into a short Base64 string.

Example

encode_int(123) returns 'B7'.


decode_int(str)
Decodes a short Base64 string into an integer.

Example

decode_int('B7') returns 123.


See also:
Customizing the TokenManager.
• Consider replacing @allow_unconfirmed_email with @login_required_allow_unconfirmed_email,
@roles_accepted_allow_unconfirmed_email, and @roles_required_allow_unconfirmed_email Pros: No
need for global setting g._flask_user_allow_unconfirmed_email Cons: Three extra decorators needed Decorator
X could test for email and then call X_allow_unconfirmed_email
• Increase test coverage - 45 lines in user_manager_views: invite_user_view - 6 lines in user_manager_views:
if invite_token and um.UserInvitationClass: - 4 lines Test with UserEmailClass - 2 lines in
user_manager__utils: confirm_email_view with invalid token - 3 lines in forms: Test with
USER_SHOW_EMAIL_DOES_NOT_EXIST - 3 lines in user_manager__views: edit_user_profile_view
• For autodocs of interfaces, init params are show twice - Idea: - conf.py: remove autoclass_content = ‘both’ - All
classes except interfaces: Move __init__ docstring to class docstring
• API docs: Submenu1: Intro, UserManager, Decorators, Forms, Views, Interfaces UserManager submenu2:
UserManager, Settings, Utils, Views Why two views? Move Managers to Intefaces

7.12 QuickStart App

Intro QuickStart App Basic App MongoDB App

The QuickStart App allows users to register and login with a username and avoids the need to configure SMTP settings.

7.12.1 Create a development environment

We recommend making use of virtualenv and virtualenvwrapper:

# Create virtual env


mkvirtualenv my_app
workon my_app

34 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

# Create working directory


mkdir -p ~dev/my_app # or mkdir C:\dev\my_app
cd ~/dev/my_app # or cd C:\dev\my_app

7.12.2 Install required Python packages

pip install Flask-User

7.12.3 Create the quickstart_app.py file

• Open your favorite editor,


• Copy the example below, and
• Save it as ~/dev/my_app/quickstart_app.py

1 # This file contains an example Flask-User application.


2 # To keep the example simple, we are applying some unusual techniques:
3 # - Placing everything in one file
4 # - Using class-based configuration (instead of file-based configuration)
5 # - Using string-based templates (instead of file-based templates)
6

7 from flask import Flask, render_template_string


8 from flask_sqlalchemy import SQLAlchemy
9 from flask_user import login_required, UserManager, UserMixin
10

11

12 # Class-based application configuration


13 class ConfigClass(object):
14 """ Flask application config """
15

16 # Flask settings
17 SECRET_KEY = 'This is an INSECURE secret!! DO NOT use this in production!!'
18

19 # Flask-SQLAlchemy settings
20 SQLALCHEMY_DATABASE_URI = 'sqlite:///quickstart_app.sqlite' # File-based SQL
˓→database

21 SQLALCHEMY_TRACK_MODIFICATIONS = False # Avoids SQLAlchemy warning


22

23 # Flask-User settings
24 USER_APP_NAME = "Flask-User QuickStart App" # Shown in and email templates
˓→and page footers

25 USER_ENABLE_EMAIL = False # Disable email authentication


26 USER_ENABLE_USERNAME = True # Enable username authentication
27 USER_REQUIRE_RETYPE_PASSWORD = False # Simplify register form
28

29

30 def create_app():
31 """ Flask application factory """
32

33 # Create Flask app load app.config


34 app = Flask(__name__)
35 app.config.from_object(__name__+'.ConfigClass')
36

37 # Initialize Flask-SQLAlchemy

7.12. QuickStart App 35


Flask-User Documentation, Release v1.0

38 db = SQLAlchemy(app)
39

40 # Define the User data-model.


41 # NB: Make sure to add flask_user UserMixin !!!
42 class User(db.Model, UserMixin):
43 __tablename__ = 'users'
44 id = db.Column(db.Integer, primary_key=True)
45 active = db.Column('is_active', db.Boolean(), nullable=False, server_default=
˓→'1')

46

47 # User authentication information


48 username = db.Column(db.String(100), nullable=False, unique=True)
49 password = db.Column(db.String(255), nullable=False, server_default='')
50 email_confirmed_at = db.Column(db.DateTime())
51

52 # User information
53 first_name = db.Column(db.String(100), nullable=False, server_default='')
54 last_name = db.Column(db.String(100), nullable=False, server_default='')
55

56 # Create all database tables


57 db.create_all()
58

59 # Setup Flask-User and specify the User data-model


60 user_manager = UserManager(app, db, User)
61

62 # The Home page is accessible to anyone


63 @app.route('/')
64 def home_page():
65 # String-based templates
66 return render_template_string("""
67 {% extends "flask_user_layout.html" %}
68 {% block content %}
69 <h2>Home page</h2>
70 <p><a href={{ url_for('user.register') }}>Register</a></p>
71 <p><a href={{ url_for('user.login') }}>Sign in</a></p>
72 <p><a href={{ url_for('home_page') }}>Home page</a> (accessible to
˓→anyone)</p>

73 <p><a href={{ url_for('member_page') }}>Member page</a> (login


˓→required)</p>

74 <p><a href={{ url_for('user.logout') }}>Sign out</a></p>


75 {% endblock %}
76 """)
77

78 # The Members page is only accessible to authenticated users via the @login_
˓→required decorator
79 @app.route('/members')
80 @login_required # User must be authenticated
81 def member_page():
82 # String-based templates
83 return render_template_string("""
84 {% extends "flask_user_layout.html" %}
85 {% block content %}
86 <h2>Members page</h2>
87 <p><a href={{ url_for('user.register') }}>Register</a></p>
88 <p><a href={{ url_for('user.login') }}>Sign in</a></p>
89 <p><a href={{ url_for('home_page') }}>Home page</a> (accessible to
˓→anyone)</p>

90 <p><a href={{ url_for('member_page') }}>Member page</a> (login


˓→required)</p>

36 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

91 <p><a href={{ url_for('user.logout') }}>Sign out</a></p>


92 {% endblock %}
93 """)
94

95 return app
96

97

98 # Start development web server


99 if __name__=='__main__':
100 app = create_app()
101 app.run(host='0.0.0.0', port=5000, debug=True)
102

Highlighted lines shows the few additional Flask-User code lines.

7.12.4 Run the QuickStart App

Run the QuickStart App with the following command:

cd ~/dev/my_app
python quickstart_app.py

And point your browser to http://localhost:5000.

7.12.5 Troubleshooting

If you receive a SQLAlchemy error message, delete the quickstart_app.sqlite file and restart the app. You may be
using an old DB schema in that file.

Intro QuickStart App Basic App MongoDB App

7.13 Basic App

Intro QuickStart App Basic App MongoDB App

The Basic App builds on the QuickStart App by adding the following features:
• Register and Login with an email
• Confirm emails, Change passwords
• Role-base authorization
• Enable translations
Unlike the QuickStart App, the Basic App requires proper SMTP settings and the installation of Flask-BabelEx.

7.13. Basic App 37


Flask-User Documentation, Release v1.0

7.13.1 Create a development environment

We recommend making use of virtualenv and virtualenvwrapper:

# Create virtual env


mkvirtualenv my_app
workon my_app

# Create working directory


mkdir -p ~dev/my_app # or mkdir C:\dev\my_app
cd ~/dev/my_app # or cd C:\dev\my_app

7.13.2 Install required Python packages

# Uninstall Flask-Babel if needed


pip uninstall Flask-Babel

# Install Flask-BabelEx and Flask-User


pip install Flask-BabelEx
pip install Flask-User

7.13.3 Create the basic_app.py file

• Open your favorite editor,


• Copy the example below, and
• Save it as ~/dev/my_app/basic_app.py

1 # This file contains an example Flask-User application.


2 # To keep the example simple, we are applying some unusual techniques:
3 # - Placing everything in one file
4 # - Using class-based configuration (instead of file-based configuration)
5 # - Using string-based templates (instead of file-based templates)
6

7 import datetime
8 from flask import Flask, request, render_template_string
9 from flask_babelex import Babel
10 from flask_sqlalchemy import SQLAlchemy
11 from flask_user import current_user, login_required, roles_required, UserManager,
˓→UserMixin

12

13

14 # Class-based application configuration


15 class ConfigClass(object):
16 """ Flask application config """
17

18 # Flask settings
19 SECRET_KEY = 'This is an INSECURE secret!! DO NOT use this in production!!'
20

21 # Flask-SQLAlchemy settings
22 SQLALCHEMY_DATABASE_URI = 'sqlite:///basic_app.sqlite' # File-based SQL
˓→database

23 SQLALCHEMY_TRACK_MODIFICATIONS = False # Avoids SQLAlchemy warning


24

38 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

25 # Flask-Mail SMTP server settings


26 MAIL_SERVER = 'smtp.gmail.com'
27 MAIL_PORT = 465
28 MAIL_USE_SSL = True
29 MAIL_USE_TLS = False
30

31 # Flask-Mail SMTP account settings


32 MAIL_USERNAME = 'email@example.com'
33 MAIL_PASSWORD = 'password'
34

35 # Flask-User settings
36 USER_APP_NAME = "Flask-User Basic App" # Shown in and email templates and
˓→page footers

37 USER_ENABLE_EMAIL = True # Enable email authentication


38 USER_ENABLE_USERNAME = False # Disable username authentication
39 USER_EMAIL_SENDER_NAME = USER_APP_NAME
40 USER_EMAIL_SENDER_EMAIL = "noreply@example.com"
41

42

43 def create_app():
44 """ Flask application factory """
45

46 # Create Flask app load app.config


47 app = Flask(__name__)
48 app.config.from_object(__name__+'.ConfigClass')
49

50 # Initialize Flask-BabelEx
51 babel = Babel(app)
52

53 # Initialize Flask-SQLAlchemy
54 db = SQLAlchemy(app)
55

56 # Define the User data-model.


57 # NB: Make sure to add flask_user UserMixin !!!
58 class User(db.Model, UserMixin):
59 __tablename__ = 'users'
60 id = db.Column(db.Integer, primary_key=True)
61 active = db.Column('is_active', db.Boolean(), nullable=False, server_default=
˓→'1')

62

63 # User authentication information


64 email = db.Column(db.String(255), nullable=False, unique=True)
65 email_confirmed_at = db.Column(db.DateTime())
66 password = db.Column(db.String(255), nullable=False, server_default='')
67

68 # User information
69 first_name = db.Column(db.String(100), nullable=False, server_default='')
70 last_name = db.Column(db.String(100), nullable=False, server_default='')
71

72 # Define the relationship to Role via UserRoles


73 roles = db.relationship('Role', secondary='user_roles')
74

75 # Define the Role data-model


76 class Role(db.Model):
77 __tablename__ = 'roles'
78 id = db.Column(db.Integer(), primary_key=True)
79 name = db.Column(db.String(50), unique=True)
80

7.13. Basic App 39


Flask-User Documentation, Release v1.0

81 # Define the UserRoles association table


82 class UserRoles(db.Model):
83 __tablename__ = 'user_roles'
84 id = db.Column(db.Integer(), primary_key=True)
85 user_id = db.Column(db.Integer(), db.ForeignKey('users.id', ondelete='CASCADE
˓→'))

86 role_id = db.Column(db.Integer(), db.ForeignKey('roles.id', ondelete='CASCADE


˓→'))

87

88 # Setup Flask-User and specify the User data-model


89 user_manager = UserManager(app, db, User)
90

91 # Create all database tables


92 db.create_all()
93

94 # Create 'member@example.com' user with no roles


95 if not User.query.filter(User.email == 'member@example.com').first():
96 user = User(
97 email='member@example.com',
98 email_confirmed_at=datetime.datetime.utcnow(),
99 password=user_manager.hash_password('Password1'),
100 )
101 db.session.add(user)
102 db.session.commit()
103

104 # Create 'admin@example.com' user with 'Admin' and 'Agent' roles


105 if not User.query.filter(User.email == 'admin@example.com').first():
106 user = User(
107 email='admin@example.com',
108 email_confirmed_at=datetime.datetime.utcnow(),
109 password=user_manager.hash_password('Password1'),
110 )
111 user.roles.append(Role(name='Admin'))
112 user.roles.append(Role(name='Agent'))
113 db.session.add(user)
114 db.session.commit()
115

116 # The Home page is accessible to anyone


117 @app.route('/')
118 def home_page():
119 return render_template_string("""
120 {% extends "flask_user_layout.html" %}
121 {% block content %}
122 <h2>{%trans%}Home page{%endtrans%}</h2>
123 <p><a href={{ url_for('user.register') }}>{%trans%}Register{
˓→%endtrans%}</a></p>

124 <p><a href={{ url_for('user.login') }}>{%trans%}Sign in{%endtrans


˓→%}</a></p>

125 <p><a href={{ url_for('home_page') }}>{%trans%}Home Page{%endtrans


˓→%}</a> (accessible to anyone)</p>

126 <p><a href={{ url_for('member_page') }}>{%trans%}Member Page{


˓→%endtrans%}</a> (login_required: member@example.com / Password1)</p>

127 <p><a href={{ url_for('admin_page') }}>{%trans%}Admin Page{


˓→%endtrans%}</a> (role_required: admin@example.com / Password1')</p>

128 <p><a href={{ url_for('user.logout') }}>{%trans%}Sign out{


˓→%endtrans%}</a></p>

129 {% endblock %}
130 """)

40 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

131

132 # The Members page is only accessible to authenticated users


133 @app.route('/members')
134 @login_required # Use of @login_required decorator
135 def member_page():
136 return render_template_string("""
137 {% extends "flask_user_layout.html" %}
138 {% block content %}
139 <h2>{%trans%}Members page{%endtrans%}</h2>
140 <p><a href={{ url_for('user.register') }}>{%trans%}Register{
˓→%endtrans%}</a></p>

141 <p><a href={{ url_for('user.login') }}>{%trans%}Sign in{%endtrans


˓→%}</a></p>

142 <p><a href={{ url_for('home_page') }}>{%trans%}Home Page{%endtrans


˓→%}</a> (accessible to anyone)</p>

143 <p><a href={{ url_for('member_page') }}>{%trans%}Member Page{


˓→%endtrans%}</a> (login_required: member@example.com / Password1)</p>

144 <p><a href={{ url_for('admin_page') }}>{%trans%}Admin Page{


˓→%endtrans%}</a> (role_required: admin@example.com / Password1')</p>

145 <p><a href={{ url_for('user.logout') }}>{%trans%}Sign out{


˓→%endtrans%}</a></p>

146 {% endblock %}
147 """)
148

149 # The Admin page requires an 'Admin' role.


150 @app.route('/admin')
151 @roles_required('Admin') # Use of @roles_required decorator
152 def admin_page():
153 return render_template_string("""
154 {% extends "flask_user_layout.html" %}
155 {% block content %}
156 <h2>{%trans%}Admin Page{%endtrans%}</h2>
157 <p><a href={{ url_for('user.register') }}>{%trans%}Register{
˓→%endtrans%}</a></p>

158 <p><a href={{ url_for('user.login') }}>{%trans%}Sign in{%endtrans


˓→%}</a></p>

159 <p><a href={{ url_for('home_page') }}>{%trans%}Home Page{%endtrans


˓→%}</a> (accessible to anyone)</p>

160 <p><a href={{ url_for('member_page') }}>{%trans%}Member Page{


˓→%endtrans%}</a> (login_required: member@example.com / Password1)</p>

161 <p><a href={{ url_for('admin_page') }}>{%trans%}Admin Page{


˓→%endtrans%}</a> (role_required: admin@example.com / Password1')</p>

162 <p><a href={{ url_for('user.logout') }}>{%trans%}Sign out{


˓→%endtrans%}</a></p>

163 {% endblock %}
164 """)
165

166 return app


167

168

169 # Start development web server


170 if __name__ == '__main__':
171 app = create_app()
172 app.run(host='0.0.0.0', port=5000, debug=True)

• Lines 25-33 configure Flask-Mail. Make sure to use the correct settings or emails will not be sent.

7.13. Basic App 41


Flask-User Documentation, Release v1.0

7.13.4 Configure Flask-Mail

Make sure to properly configure Flask-Mail settings:

# Flask-Mail SMTP Server settings


MAIL_SERVER =
MAIL_PORT =
MAIL_USE_SSL =
MAIL_USE_TLS =

# Flask-Mail SMTP Account settings


MAIL_USERNAME =
MAIL_PASSWORD =

Note: Gmail and Yahoo mail nowadays disable SMTP requests by default. Search the web for ‘Gmail less secure
apps’ or ‘Yahoo less secure apps’ to learn how to change this default setting for your account.
See QuickStart App for an example without SMTP.

7.13.5 Configure your browser

If you want to see translation in action, you will need to change and prioritize a supported language (one that is other
than ‘English’) in your browser language preferences.
For Google Chrome:
• Chrome > Preferences. Search for ‘Language’.
• Expand the ‘Language’ bar > Add languages.
• Check the ‘Dutch’ checkbox > Add.
• Make sure to move it to the top: Three dots > Move to top.

7.13.6 Run the Basic App

Run the Basic App with the following command:

cd ~/dev/my_app
python basic_app.py

And point your browser to http://localhost:5000.

7.13.7 Troubleshooting

If you receive an EmailError message, or if the Registration form does not respond quickly then you may have specified
incorrect SMTP settings.
If you receive a SQLAlchemy error message, you may be using an old DB schema. Delete the quickstart_app.sqlite
file and restart the app.
If you don’t see any translations, you may not have installed Flask-BabelEx, or you may not have prioritized a
supported language in your browser settings.

42 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

Intro QuickStart App Basic App MongoDB App

7.14 MongoDB App

Intro QuickStart App Basic App MongoDB App

Flask-User can work with MongoDB databases by replacing the default SQLDbAdapter with the provided MongoD-
bAdapter.
The MONGODB_SETTINGS in this example requires a MongoDB server running on localhost:27017 (its default).

7.14.1 Install Flask-User

We recommend making use of virtualenv and virtualenvwrapper:


# Create virtual env
mkvirtualenv my_app
workon my_app

# Create working directory


mkdir -p ~dev/my_app # or mkdir C:\dev\my_app
cd ~/dev/my_app # or cd C:\dev\my_app

# Install Flask-User
pip install flask-user

7.14.2 Install Flask-MongoEngine

# Install Flask-MongoEngine
pip install Flask-MongoEngine

7.14.3 Create the mongodb_app.py file

• Open your favorite editor,


• Copy the example below, and
• Save it as ~/dev/my_app/mongodb_app.py
1 # This file contains an example Flask-User application.
2 # To keep the example simple, we are applying some unusual techniques:
3 # - Placing everything in one file
4 # - Using class-based configuration (instead of file-based configuration)
5 # - Using string-based templates (instead of file-based templates)
6

7 from flask import Flask, render_template_string


8 from flask_mongoengine import MongoEngine
9 from flask_user import login_required, UserManager, UserMixin
10

11

12 # Class-based application configuration

7.14. MongoDB App 43


Flask-User Documentation, Release v1.0

13 class ConfigClass(object):
14 """ Flask application config """
15

16 # Flask settings
17 SECRET_KEY = 'This is an INSECURE secret!! DO NOT use this in production!!'
18

19 # Flask-MongoEngine settings
20 MONGODB_SETTINGS = {
21 'db': 'tst_app',
22 'host': 'mongodb://localhost:27017/tst_app'
23 }
24

25 # Flask-User settings
26 USER_APP_NAME = "Flask-User MongoDB App" # Shown in and email templates and
˓→page footers

27 USER_ENABLE_EMAIL = False # Disable email authentication


28 USER_ENABLE_USERNAME = True # Enable username authentication
29 USER_REQUIRE_RETYPE_PASSWORD = False # Simplify register form
30

31

32 def create_app():
33 """ Flask application factory """
34

35 # Setup Flask and load app.config


36 app = Flask(__name__)
37 app.config.from_object(__name__+'.ConfigClass')
38

39 # Setup Flask-MongoEngine
40 db = MongoEngine(app)
41

42 # Define the User document.


43 # NB: Make sure to add flask_user UserMixin !!!
44 class User(db.Document, UserMixin):
45 active = db.BooleanField(default=True)
46

47 # User authentication information


48 username = db.StringField(default='')
49 password = db.StringField()
50

51 # User information
52 first_name = db.StringField(default='')
53 last_name = db.StringField(default='')
54

55 # Relationships
56 roles = db.ListField(db.StringField(), default=[])
57

58 # Setup Flask-User and specify the User data-model


59 user_manager = UserManager(app, db, User)
60

61 # The Home page is accessible to anyone


62 @app.route('/')
63 def home_page():
64 # String-based templates
65 return render_template_string("""
66 {% extends "flask_user_layout.html" %}
67 {% block content %}
68 <h2>Home page</h2>
69 <p><a href={{ url_for('user.register') }}>Register</a></p>

44 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

70 <p><a href={{ url_for('user.login') }}>Sign in</a></p>


71 <p><a href={{ url_for('home_page') }}>Home page</a> (accessible to
˓→ anyone)</p>
72 <p><a href={{ url_for('member_page') }}>Member page</a> (login
˓→ required)</p>
73 <p><a href={{ url_for('user.logout') }}>Sign out</a></p>
74 {% endblock %}
75 """)
76

77 # The Members page is only accessible to authenticated users via the @login_
˓→required decorator
78 @app.route('/members')
79 @login_required # User must be authenticated
80 def member_page():
81 # String-based templates
82 return render_template_string("""
83 {% extends "flask_user_layout.html" %}
84 {% block content %}
85 <h2>Members page</h2>
86 <p><a href={{ url_for('user.register') }}>Register</a></p>
87 <p><a href={{ url_for('user.login') }}>Sign in</a></p>
88 <p><a href={{ url_for('home_page') }}>Home page</a> (accessible to
˓→anyone)</p>

89 <p><a href={{ url_for('member_page') }}>Member page</a> (login


˓→required)</p>

90 <p><a href={{ url_for('user.logout') }}>Sign out</a></p>


91 {% endblock %}
92 """)
93

94 return app
95

96

97 # Start development web server


98 if __name__=='__main__':
99 app = create_app()
100 app.run(host='0.0.0.0', port=5000, debug=True)
101

Highlighted lines shows the few additional Flask-User code lines.

7.14.4 Run the MongoDB App

Run the MongoDB App with the following command:

cd ~/dev/my_app
python mongodb_app.py

And point your browser to http://localhost:5000.

7.14.5 Troubleshooting

[TBD]

Intro QuickStart App Basic App MongoDB App

7.14. MongoDB App 45


Flask-User Documentation, Release v1.0

7.15 DbAdapter Interface

The DbAdapterInterface class defines an interface to find, add, update and remove persistent database objects, while
shielding the Flask-User code from the underlying implementation.
class DbAdapterInterface(app, db)
Define the DbAdapter interface to manage objects in various databases.
This interface supports session-based ODMs (db.session.add()/commit()) as well as object-based
ODMs (object.save()).
Parameters
• app (Flask) – The Flask appliation instance.
• db – The object-database mapper instance.
__init__(app, db)
Parameters
• app (Flask) – The Flask appliation instance.
• db – The object-database mapper instance.
add_object(object)
Add a new object to the database.

Session-based ODMs would call something like db.session.add(object).


Object-based ODMs would call something like object.save().

commit()
Save all modified session objects to the database.

Session-based ODMs would call something like db.session.commit().


Object-based ODMs would do nothing.

delete_object(object)
Delete object from database.
find_objects(ObjectClass, **kwargs)
Retrieve all objects of type ObjectClass, matching the specified filters in **kwargs – case sensitive.
find_first_object(ObjectClass, **kwargs)
Retrieve the first object of type ObjectClass, matching the specified filters in **kwargs – case sen-
sitive.
get_object(ObjectClass, id)
Retrieve object of type ObjectClass by id.

Returns object on success.


Returns None otherwise.

46 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

save_object(object)
Save object to database.

Session-based ODMs would do nothing.


Object-based ODMs would do something like object.save().

create_all_tables()
Create database tables for all known database data-models.
drop_all_tables()
Drop all tables.

Warning: ALL DATA WILL BE LOST. Use only for automated testing.

7.15.1 Implementing a CustomDbAdapter

You can write you own DbAdapter implementation by defining a CustomDbAdapter class and configure Flask-User
to use this class like so:

# Define a CustomDbAdapter
from flask_user.db_adapters import DbAdapterInterface
class CustomDbAdapter(DbAdapterInterface):
...

# Setup Flask-User
user_manager = UserManager(app, db, User)

# Customize Flask-User
user_manager.db_adapter = CustomDbAdapter(app)

For an example, see the SQLDbAdapter() implementation.

7.16 View decorators

Flask-User view decorators serve as the gatekeepers to prevent unauthenticated or unauthorized users from accessing
certain views.

Important: The @route decorator must always be the first view decorator in a list of view decorators (because it’s
used to map the function below itself to a URL).

login_required(view_function)
This decorator ensures that the current user is logged in.
Example:

@route('/member_page')
@login_required
def member_page(): # User must be logged in
...

7.16. View decorators 47


Flask-User Documentation, Release v1.0

If USER_ENABLE_EMAIL is True and USER_ENABLE_CONFIRM_EMAIL is True, this view decorator


also ensures that the user has a confirmed email address.

Calls unauthorized_view() when the user is not logged in or when the user has not confirmed their email
address.
Calls the decorated view otherwise.

roles_accepted(*role_names)

This decorator ensures that the current user is logged in,


and has at least one of the specified roles (OR operation).
Example:

@route('/edit_article')
@roles_accepted('Writer', 'Editor')
def edit_article(): # User must be 'Writer' OR 'Editor'
...

Calls unauthenticated_view() when the user is not logged in or when user has not confirmed their email
address.
Calls unauthorized_view() when the user does not have the required roles.
Calls the decorated view otherwise.

roles_required(*role_names)

This decorator ensures that the current user is logged in,


and has all of the specified roles (AND operation).
Example:

@route('/escape')
@roles_required('Special', 'Agent')
def escape_capture(): # User must be 'Special' AND 'Agent'
...

Calls unauthenticated_view() when the user is not logged in or when user has not confirmed their email
address.
Calls unauthorized_view() when the user does not have the required roles.
Calls the decorated view otherwise.

allow_unconfirmed_email(view_function)
This decorator ensures that the user is logged in, but allows users with or without a confirmed email addresses
to access this particular view.
It works in tandem with the USER_ALLOW_LOGIN_WITHOUT_CONFIRMED_EMAIL=True setting.

48 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

Caution:
Use USER_ALLOW_LOGIN_WITHOUT_CONFIRMED_EMAIL=True and
@allow_unconfirmed_email with caution, as they relax security requirements.
Make sure that decorated views never call other views directly. Allways use redirect() to ensure
proper view protection.

Example:

@route('/show_promotion')
@allow_unconfirmed_emails
def show_promotion(): # Logged in, with or without
... # confirmed email address

It can also precede the @roles_required and @roles_accepted view decorators:

@route('/show_promotion')
@allow_unconfirmed_emails
@roles_required('Visitor')
def show_promotion(): # Logged in, with or without
... # confirmed email address

Calls unauthorized_view() when the user is not logged in.


Calls the decorated view otherwise.

7.17 EmailAdapter Interface

The EmailAdapterInterface class defines an interface to send email messages while shielding the Flask-User code from
the underlying implementation.
class EmailAdapterInterface(app)
Define the EmailAdapter interface to send emails through various email services.
Parameters app (Flask) – The Flask application instance.
__init__(app)
Parameters app (Flask) – The Flask application instance.
send_email_message(recipient, subject, html_message, text_message, sender_email, sender_name)
Send email message via an email mailer.
Parameters
• recipient – Email address or tuple of (Name, Email-address).
• subject – Subject line.
• html_message – The message body in HTML.
• text_message – The message body in plain text.

Tip:

7.17. EmailAdapter Interface 49


Flask-User Documentation, Release v1.0

def __init__(self, app):


self.app = app
self.sender_name = self.app.user_manager.USER_EMAIL_SENDER_NAME
self.sender_email = self.app.user_manager.USER_EMAIL_SENDER_EMAIL

def send_email_message(...):
# use self.sender_name and self.sender_email here...

7.17.1 Implementing a CustomEmailAdapter

You can write you own EmailAdapter implementation by defining a CustomEmailAdapter class and configure Flask-
User to use this class like so:
# Define a CustomEmailAdapter
from flask_user.email_adapters import EmailAdapterInterface
class CustomEmailAdapter(EmailAdapterInterface):
...

# Setup Flask-User
user_manager = UserManager(app, db, User)

# Customize Flask-User
user_manager.email_adapter = CustomDbAdapter(app)

For an example, see the SMTPEmailAdapter() implementation.

7.18 Forms

Below is a complete list of customizable Flask-User forms.

7.18.1 AddEmailForm

class AddEmailForm(formdata=<object object>, **kwargs)


Add an email address form.

7.18.2 ChangeUsernameForm

class ChangeUsernameForm(formdata=<object object>, **kwargs)


Change username form.

7.18.3 EditUserProfileForm

class EditUserProfileForm(formdata=<object object>, **kwargs)


Edit user profile form.

50 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

7.18.4 ForgotPasswordForm

class ForgotPasswordForm(formdata=<object object>, **kwargs)


Forgot password form.

7.18.5 InviteUserForm

class InviteUserForm(formdata=<object object>, **kwargs)


Invite new user form.

7.18.6 LoginForm

class LoginForm(*args, **kwargs)


Login form.

7.18.7 RegisterForm

class RegisterForm(formdata=<object object>, **kwargs)


Register new user form.

7.18.8 ResendEmailConfirmationForm

class ResendEmailConfirmationForm(formdata=<object object>, **kwargs)


Resend email confirmation form.

7.18.9 ResetPasswordForm

class ResetPasswordForm(formdata=<object object>, **kwargs)


Reset password form.

7.19 UserManager Class

The UserManager class implements most of the Flask-User functionality.


Flask-User can be customized by extending or overriding any of the methods listed below.
class UserManager(app, db, UserClass, **kwargs)
Customizable User Authentication and Management.
Parameters

7.19. UserManager Class 51


Flask-User Documentation, Release v1.0

• app (Flask) – The Flask application instance.


• db – An Object-Database Mapper instance such as SQLAlchemy or MongoEngine.
• UserClass – The User class (not an instance!).
Keyword Arguments
• UserEmailClass – The optional UserEmail class (not an instance!). Required for the
‘multiple emails per user’ feature.
• UserInvitationClass – The optional UserInvitation class (not an instance!). Re-
quired for the ‘register by invitation’ feature.

Example

user_manager = UserManager(app, db, User, UserEmailClass=UserEmail)


Customizable UserManager methods

customize(app)
Override this method to customize properties.
Example:
# Customize Flask-User
class CustomUserManager(UserManager):

def customize(self, app):

# Add custom managers and email mailers here


self.email_manager = CustomEmailManager(app)
self.password_manager = CustomPasswordManager(app)
self.token_manager = CustomTokenManager(app)
self.email_adapter = CustomEmailAdapter(app)

# Setup Flask-User
user_manager = CustomUserManager(app, db, User)

password_validator(form, field)
Ensure that passwords have one lowercase letter, one uppercase letter and one digit.
Override this method to customize the password validator.
username_validator(form, field)
Ensure that Usernames contains at least 3 alphanumeric characters.
Override this method to customize the username validator.
See also:
• UserManager Settings
• UserManager View methods
• UserManager Utility methods

7.20 UserManager Settings

Below is a complete list of configurable Flask-User settings.

52 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

| The UserManager__Settings class mixes into the UserManager class.


| Mixins allow for maintaining code and docs across several files.

class UserManager__Settings
Flask-User settings and their defaults.

Feature settings

USER_ENABLE_EMAIL = True

Allow users to login and register with an email address


USER_ENABLE_MULTIPLE_EMAILS = False

Allow users to associate multiple email addresses with one user account.
Depends on USER_ENABLE_EMAIL=True
USER_ENABLE_USERNAME = True

Allow users to login and register with a username


USER_ENABLE_CHANGE_USERNAME = True

Allow users to change their username.


Depends on USER_ENABLE_USERNAME=True.
USER_ENABLE_CHANGE_PASSWORD = True

Allow users to change their password.


USER_ENABLE_CONFIRM_EMAIL = True

Enable email confirmation emails to be sent.


Depends on USER_ENABLE_EMAIL=True.
USER_ENABLE_FORGOT_PASSWORD = True

Allow users to reset their passwords.


Depends on USER_ENABLE_EMAIL=True.
USER_ENABLE_INVITE_USER = False

Allow unregistered users to be invited.


USER_ENABLE_REGISTER = True

Allow unregistered users to register.


USER_ENABLE_REMEMBER_ME = True

Remember user sessions across browser restarts.

7.20. UserManager Settings 53


Flask-User Documentation, Release v1.0

Generic settings and their defaults

USER_APP_NAME = 'USER_APP_NAME'
The application name displayed in email templates and page template footers.
USER_AUTO_LOGIN = True
Automatic sign-in if the user session has not expired.
USER_AUTO_LOGIN_AFTER_CONFIRM = True
Automatic sign-in after a user confirms their email address.
USER_AUTO_LOGIN_AFTER_REGISTER = True
Automatic sign-in after a user registers.
USER_AUTO_LOGIN_AFTER_RESET_PASSWORD = True
Automatic sign-in after a user resets their password.
USER_AUTO_LOGIN_AT_LOGIN = True
Automatic sign-in at the login form (if the user session has not expired).
USER_EMAIL_SENDER_EMAIL = ''

Sender’s email address, used by the EmailAdapters.


Required for sending emails.
Derived from MAIL_DEFAULT_SENDER or DEFAULT_MAIL_SENDER when specified.
USER_EMAIL_SENDER_NAME = ''

Sender’s name, user by the EmailAdapters.


Optional. Defaults to USER_APP_NAME setting.
USER_SEND_PASSWORD_CHANGED_EMAIL = True

Send notification email after a password change.


Depends on USER_ENABLE_EMAIL=True.
USER_SEND_REGISTERED_EMAIL = True

Send notification email after a registration.


Depends on USER_ENABLE_EMAIL=True.
USER_SEND_USERNAME_CHANGED_EMAIL = True

Send notification email after a username change.


Depends on USER_ENABLE_EMAIL=True.
USER_REQUIRE_INVITATION = False

Only invited users may register.


Depends on USER_ENABLE_EMAIL=True.
USER_ALLOW_LOGIN_WITHOUT_CONFIRMED_EMAIL = False

Ensure that users can login only with a confirmed email address.
Depends on USER_ENABLE_EMAIL=True.

54 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

This setting works in tandem with the @allow_unconfirmed_emails view decorator to allow users
without confirmed email addresses to access certain views.

Caution:
Use USER_ALLOW_LOGIN_WITHOUT_CONFIRMED_EMAIL=True and
@allow_unconfirmed_email with caution, as they relax security requirements.
Make sure that decorated views never call other views directly. Allways se redirect() to ensure
proper view protection.

USER_REQUIRE_RETYPE_PASSWORD = True

Require users to retype their password.


Affects registration, change password and reset password forms.
USER_SHOW_EMAIL_DOES_NOT_EXIST = False

Show ‘Email does not exist’ message instead of ‘Incorrect Email or password’.
Depends on USER_ENABLE_EMAIL=True.
USER_SHOW_USERNAME_DOES_NOT_EXIST = False

Show ‘Username does not exist’ message instead of ‘Incorrect Username or password’.
Depends on USER_ENABLE_USERNAME=True.
USER_CONFIRM_EMAIL_EXPIRATION = 172800

Email confirmation token expiration in seconds.


Default is 2 days (2*24*3600 seconds).
USER_INVITE_EXPIRATION = 7776000

Invitation token expiration in seconds.


Default is 90 days (90*24*3600 seconds).
USER_RESET_PASSWORD_EXPIRATION = 172800

Reset password token expiration in seconds.


Default is 2 days (2*24*3600 seconds).
USER_USER_SESSION_EXPIRATION = 3600

User session token expiration in seconds.


Default is 1 hour (1*3600 seconds).

Password hash settings

USER_PASSLIB_CRYPTCONTEXT_SCHEMES = ['bcrypt']

List of accepted password hashes.

7.20. UserManager Settings 55


Flask-User Documentation, Release v1.0

See Passlib CryptContext docs on Constructor Keyword ‘’schemes’‘‘ <http://passlib.readthedocs.io/en/


stable/lib/passlib.context.html?highlight=cryptcontext#constructor-keywords>‘_
Example: ['bcrypt', 'argon2']
Creates new hashes with ‘bcrypt’ and verifies existing hashes with ‘bcrypt’ and ‘argon2’.
USER_PASSLIB_CRYPTCONTEXT_KEYWORDS = {}

Dictionary of CryptContext keywords and hash options.


See Passlib CryptContext docs on Constructor Keywords
and Passlib CryptContext docs on Algorithm Options
Example: dict(bcrypt__rounds=12, argon2__time_cost=2,
argon2__memory_cost=512)
USER_CHANGE_PASSWORD_URL = '/user/change-password'
USER_CHANGE_USERNAME_URL = '/user/change-username'
USER_CONFIRM_EMAIL_URL = '/user/confirm-email/<token>'
USER_EDIT_USER_PROFILE_URL = '/user/edit_user_profile'
USER_EMAIL_ACTION_URL = '/user/email/<id>/<action>'
USER_FORGOT_PASSWORD_URL = '/user/forgot-password'
USER_INVITE_USER_URL = '/user/invite'
USER_LOGIN_URL = '/user/sign-in'
USER_LOGOUT_URL = '/user/sign-out'
USER_MANAGE_EMAILS_URL = '/user/manage-emails'
USER_REGISTER_URL = '/user/register'
USER_RESEND_EMAIL_CONFIRMATION_URL = '/user/resend-email-confirmation'
USER_RESET_PASSWORD_URL = '/user/reset-password/<token>'

Template file settings

USER_CHANGE_PASSWORD_TEMPLATE = 'flask_user/change_password.html'
USER_CHANGE_USERNAME_TEMPLATE = 'flask_user/change_username.html'
USER_EDIT_USER_PROFILE_TEMPLATE = 'flask_user/edit_user_profile.html'
USER_FORGOT_PASSWORD_TEMPLATE = 'flask_user/forgot_password.html'
USER_INVITE_USER_TEMPLATE = 'flask_user/invite_user.html'
USER_LOGIN_TEMPLATE = 'flask_user/login.html'
USER_MANAGE_EMAILS_TEMPLATE = 'flask_user/manage_emails.html'
USER_REGISTER_TEMPLATE = 'flask_user/register.html'
USER_RESEND_CONFIRM_EMAIL_TEMPLATE = 'flask_user/resend_confirm_email.html'
USER_RESET_PASSWORD_TEMPLATE = 'flask_user/reset_password.html'

Email template file settings

56 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

USER_CONFIRM_EMAIL_TEMPLATE = 'flask_user/emails/confirm_email'
USER_INVITE_USER_EMAIL_TEMPLATE = 'flask_user/emails/invite_user'
USER_PASSWORD_CHANGED_EMAIL_TEMPLATE = 'flask_user/emails/password_changed'
USER_REGISTERED_EMAIL_TEMPLATE = 'flask_user/emails/registered'
USER_RESET_PASSWORD_EMAIL_TEMPLATE = 'flask_user/emails/reset_password'
USER_USERNAME_CHANGED_EMAIL_TEMPLATE = 'flask_user/emails/username_changed'

FLask endpoint settings

USER_AFTER_CHANGE_PASSWORD_ENDPOINT = ''
USER_AFTER_CHANGE_USERNAME_ENDPOINT = ''
USER_AFTER_CONFIRM_ENDPOINT = ''
USER_AFTER_EDIT_USER_PROFILE_ENDPOINT = ''
USER_AFTER_FORGOT_PASSWORD_ENDPOINT = ''
USER_AFTER_LOGIN_ENDPOINT = ''
USER_AFTER_LOGOUT_ENDPOINT = ''
USER_AFTER_REGISTER_ENDPOINT = ''
USER_AFTER_RESEND_EMAIL_CONFIRMATION_ENDPOINT = ''
USER_AFTER_RESET_PASSWORD_ENDPOINT = ''
USER_AFTER_INVITE_ENDPOINT = ''
USER_UNAUTHENTICATED_ENDPOINT = 'user.login'
USER_UNAUTHORIZED_ENDPOINT = ''
See also:
• UserManager Class
• UserManager View methods
• UserManager Utility methods

7.21 UserManager Utility methods

Below is a complete list of Flask-User utility methods.

| The UserManager__Utils class mixes into the UserManager class.


| Mixins allow for maintaining code and docs across several files.

class UserManager__Utils
Flask-User utility methods.
call_or_get(function_or_property)

Calls function_or_property if it’s a function.


Gets function_or_property otherwise.

7.21. UserManager Utility methods 57


Flask-User Documentation, Release v1.0

In Flask-Login 0.2 is_authenticated and is_active were implemented as functions, while in


0.3+ they are implemented as properties.
Example:

if self.call_or_get(current_user.is_authenticated):
pass

email_is_available(new_email)
Check if new_email is available.

Returns True if new_email does not exist or belongs to the current user.
Return False otherwise.

generate_token(*args)
Convenience method that calls self.token_manager.generate_token(*args).
hash_password(password)
Convenience method that calls self.password_manager.hash_password(password).
make_safe_url(url)
Makes a URL safe by removing optional hostname and port.

Example

make_safe_url('https://hostname:80/path1/path2?q1=v1&q2=v2#fragment')
returns '/path1/path2?q1=v1&q2=v2#fragment'

Override this method if you need to allow a list of safe hostnames.


prepare_domain_translations()
Set domain_translations for current request context.
verify_password(password, password_hash)
Convenience method that calls self.password_manager.verify_password(password, password_hash).
verify_token(token, expiration_in_seconds)
Convenience method that calls self.token_manager.verify_token(token, expiration_in_seconds).
See also:
• UserManager Class
• UserManager Settings
• UserManager View methods

7.22 UserManager View methods

Below is a complete list of customizable Flask-User views.

| The UserManager__Views class mixes into the UserManager class.


| Mixins allow for maintaining code and docs across several files.

58 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

class UserManager__Views
Flask-User views.
change_password_view(*args, **kwargs)
Prompt for old password and new password and change the user’s password.
change_username_view(*args, **kwargs)
Prompt for new username and old password and change the user’s username.
confirm_email_view(token)
Verify email confirmation token and activate the user account.
email_action_view(*args, **kwargs)
Perform action ‘action’ on UserEmail object ‘id’
forgot_password_view()
Prompt for email and send reset password email.
invite_user_view(*args, **kwargs)
Allows users to send invitations to register an account
login_view()
Prepare and process the login form.
logout_view()
Process the logout link.
register_view()
Display registration form and create new User.
resend_email_confirmation_view()
Prompt for email and re-send email conformation email.
reset_password_view(token)
Verify the password reset token, Prompt for new password, and set the user’s password.
unauthenticated_view()
Prepare a Flash message and redirect to USER_UNAUTHENTICATED_ENDPOINT
unauthorized_view()
Prepare a Flash message and redirect to USER_UNAUTHORIZED_ENDPOINT
See also:
• UserManager Class
• UserManager Settings
• UserManager Utility methods

7.23 Configuring settings

Intro Settings Forms Emails Advanced

Flask-User default features and settings can overridden through the app config:

# Customize Flask-User settings


USER_ENABLE_EMAIL = True
USER_ENABLE_USERNAME = False

7.23. Configuring settings 59


Flask-User Documentation, Release v1.0

7.23.1 Flask-User settings

Below is a complete list of configurable Flask-User settings and their defaults.


Note: Ignore the __Settings part of the class name. It’s a trick we use to split the code and docs across several files.
class UserManager__Settings
Flask-User settings and their defaults.

Feature settings

USER_ENABLE_EMAIL = True

Allow users to login and register with an email address


USER_ENABLE_MULTIPLE_EMAILS = False

Allow users to associate multiple email addresses with one user account.
Depends on USER_ENABLE_EMAIL=True
USER_ENABLE_USERNAME = True

Allow users to login and register with a username


USER_ENABLE_CHANGE_USERNAME = True

Allow users to change their username.


Depends on USER_ENABLE_USERNAME=True.
USER_ENABLE_CHANGE_PASSWORD = True

Allow users to change their password.


USER_ENABLE_CONFIRM_EMAIL = True

Enable email confirmation emails to be sent.


Depends on USER_ENABLE_EMAIL=True.
USER_ENABLE_FORGOT_PASSWORD = True

Allow users to reset their passwords.


Depends on USER_ENABLE_EMAIL=True.
USER_ENABLE_INVITE_USER = False

Allow unregistered users to be invited.


USER_ENABLE_REGISTER = True

Allow unregistered users to register.


USER_ENABLE_REMEMBER_ME = True

Remember user sessions across browser restarts.

60 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

Generic settings and their defaults

USER_APP_NAME = 'USER_APP_NAME'
The application name displayed in email templates and page template footers.
USER_AUTO_LOGIN = True
Automatic sign-in if the user session has not expired.
USER_AUTO_LOGIN_AFTER_CONFIRM = True
Automatic sign-in after a user confirms their email address.
USER_AUTO_LOGIN_AFTER_REGISTER = True
Automatic sign-in after a user registers.
USER_AUTO_LOGIN_AFTER_RESET_PASSWORD = True
Automatic sign-in after a user resets their password.
USER_AUTO_LOGIN_AT_LOGIN = True
Automatic sign-in at the login form (if the user session has not expired).
USER_EMAIL_SENDER_EMAIL = ''

Sender’s email address, used by the EmailAdapters.


Required for sending emails.
Derived from MAIL_DEFAULT_SENDER or DEFAULT_MAIL_SENDER when specified.
USER_EMAIL_SENDER_NAME = ''

Sender’s name, user by the EmailAdapters.


Optional. Defaults to USER_APP_NAME setting.
USER_SEND_PASSWORD_CHANGED_EMAIL = True

Send notification email after a password change.


Depends on USER_ENABLE_EMAIL=True.
USER_SEND_REGISTERED_EMAIL = True

Send notification email after a registration.


Depends on USER_ENABLE_EMAIL=True.
USER_SEND_USERNAME_CHANGED_EMAIL = True

Send notification email after a username change.


Depends on USER_ENABLE_EMAIL=True.
USER_REQUIRE_INVITATION = False

Only invited users may register.


Depends on USER_ENABLE_EMAIL=True.
USER_ALLOW_LOGIN_WITHOUT_CONFIRMED_EMAIL = False

Ensure that users can login only with a confirmed email address.
Depends on USER_ENABLE_EMAIL=True.

7.23. Configuring settings 61


Flask-User Documentation, Release v1.0

This setting works in tandem with the @allow_unconfirmed_emails view decorator to allow users
without confirmed email addresses to access certain views.

Caution:
Use USER_ALLOW_LOGIN_WITHOUT_CONFIRMED_EMAIL=True and
@allow_unconfirmed_email with caution, as they relax security requirements.
Make sure that decorated views never call other views directly. Allways se redirect() to ensure
proper view protection.

USER_REQUIRE_RETYPE_PASSWORD = True

Require users to retype their password.


Affects registration, change password and reset password forms.
USER_SHOW_EMAIL_DOES_NOT_EXIST = False

Show ‘Email does not exist’ message instead of ‘Incorrect Email or password’.
Depends on USER_ENABLE_EMAIL=True.
USER_SHOW_USERNAME_DOES_NOT_EXIST = False

Show ‘Username does not exist’ message instead of ‘Incorrect Username or password’.
Depends on USER_ENABLE_USERNAME=True.
USER_CONFIRM_EMAIL_EXPIRATION = 172800

Email confirmation token expiration in seconds.


Default is 2 days (2*24*3600 seconds).
USER_INVITE_EXPIRATION = 7776000

Invitation token expiration in seconds.


Default is 90 days (90*24*3600 seconds).
USER_RESET_PASSWORD_EXPIRATION = 172800

Reset password token expiration in seconds.


Default is 2 days (2*24*3600 seconds).
USER_USER_SESSION_EXPIRATION = 3600

User session token expiration in seconds.


Default is 1 hour (1*3600 seconds).

Password hash settings

USER_PASSLIB_CRYPTCONTEXT_SCHEMES = ['bcrypt']

List of accepted password hashes.

62 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

See Passlib CryptContext docs on Constructor Keyword ‘’schemes’‘‘ <http://passlib.readthedocs.io/en/


stable/lib/passlib.context.html?highlight=cryptcontext#constructor-keywords>‘_
Example: ['bcrypt', 'argon2']
Creates new hashes with ‘bcrypt’ and verifies existing hashes with ‘bcrypt’ and ‘argon2’.
USER_PASSLIB_CRYPTCONTEXT_KEYWORDS = {}

Dictionary of CryptContext keywords and hash options.


See Passlib CryptContext docs on Constructor Keywords
and Passlib CryptContext docs on Algorithm Options
Example: dict(bcrypt__rounds=12, argon2__time_cost=2,
argon2__memory_cost=512)
USER_CHANGE_PASSWORD_URL = '/user/change-password'
USER_CHANGE_USERNAME_URL = '/user/change-username'
USER_CONFIRM_EMAIL_URL = '/user/confirm-email/<token>'
USER_EDIT_USER_PROFILE_URL = '/user/edit_user_profile'
USER_EMAIL_ACTION_URL = '/user/email/<id>/<action>'
USER_FORGOT_PASSWORD_URL = '/user/forgot-password'
USER_INVITE_USER_URL = '/user/invite'
USER_LOGIN_URL = '/user/sign-in'
USER_LOGOUT_URL = '/user/sign-out'
USER_MANAGE_EMAILS_URL = '/user/manage-emails'
USER_REGISTER_URL = '/user/register'
USER_RESEND_EMAIL_CONFIRMATION_URL = '/user/resend-email-confirmation'
USER_RESET_PASSWORD_URL = '/user/reset-password/<token>'

Template file settings

USER_CHANGE_PASSWORD_TEMPLATE = 'flask_user/change_password.html'
USER_CHANGE_USERNAME_TEMPLATE = 'flask_user/change_username.html'
USER_EDIT_USER_PROFILE_TEMPLATE = 'flask_user/edit_user_profile.html'
USER_FORGOT_PASSWORD_TEMPLATE = 'flask_user/forgot_password.html'
USER_INVITE_USER_TEMPLATE = 'flask_user/invite_user.html'
USER_LOGIN_TEMPLATE = 'flask_user/login.html'
USER_MANAGE_EMAILS_TEMPLATE = 'flask_user/manage_emails.html'
USER_REGISTER_TEMPLATE = 'flask_user/register.html'
USER_RESEND_CONFIRM_EMAIL_TEMPLATE = 'flask_user/resend_confirm_email.html'
USER_RESET_PASSWORD_TEMPLATE = 'flask_user/reset_password.html'

Email template file settings

7.23. Configuring settings 63


Flask-User Documentation, Release v1.0

USER_CONFIRM_EMAIL_TEMPLATE = 'flask_user/emails/confirm_email'
USER_INVITE_USER_EMAIL_TEMPLATE = 'flask_user/emails/invite_user'
USER_PASSWORD_CHANGED_EMAIL_TEMPLATE = 'flask_user/emails/password_changed'
USER_REGISTERED_EMAIL_TEMPLATE = 'flask_user/emails/registered'
USER_RESET_PASSWORD_EMAIL_TEMPLATE = 'flask_user/emails/reset_password'
USER_USERNAME_CHANGED_EMAIL_TEMPLATE = 'flask_user/emails/username_changed'

FLask endpoint settings

USER_AFTER_CHANGE_PASSWORD_ENDPOINT = ''
USER_AFTER_CHANGE_USERNAME_ENDPOINT = ''
USER_AFTER_CONFIRM_ENDPOINT = ''
USER_AFTER_EDIT_USER_PROFILE_ENDPOINT = ''
USER_AFTER_FORGOT_PASSWORD_ENDPOINT = ''
USER_AFTER_LOGIN_ENDPOINT = ''
USER_AFTER_LOGOUT_ENDPOINT = ''
USER_AFTER_REGISTER_ENDPOINT = ''
USER_AFTER_RESEND_EMAIL_CONFIRMATION_ENDPOINT = ''
USER_AFTER_RESET_PASSWORD_ENDPOINT = ''
USER_AFTER_INVITE_ENDPOINT = ''
USER_UNAUTHENTICATED_ENDPOINT = 'user.login'
USER_UNAUTHORIZED_ENDPOINT = ''
To keep the code base simple and robust, we offer no easy way to change the ‘/user’ base URLs or the ‘/flask_user’
base directories in bulk. Please copy them from this page, then use your editor to bulk-change these settings.

Intro Settings Forms Emails Advanced

7.24 Customizing Emails

Intro Settings Forms Emails Advanced

7.24.1 Customizing EmailAdapters

Flask-User ships with the following EmailAdapters: - SendgridEmailAdapter to send email messages via Sendgrid -
SendmailEmailAdapter to send email messages via Sendmail - SMTPEmailAdapter to send email messages via SMTP
Flask-User works with the SMTPEmailAdapter by default, but another EmailAdapter can be configured like so:

64 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

# Setup Flask-User
user_manager = UserManager(app, db, User)

# Customize Flask-User
from flask_user.email_adapters import SendgridEmailAdapter
user_manager.email_adapter = SendgridEmailAdapter(app)

7.24.2 Customizing Email messages

Flask-User currently offers the following types of email messages:

confirm_email # Sent after a user submitted a registration form


# - Requires USER_ENABLE_EMAIL = True
# - Requires USER_ENABLE_CONFIRM_EMAIL = True

forgot_password # Sent after a user submitted a forgot password form


# - Requires USER_ENABLE_EMAIL = True
# - Requires USER_ENABLE_FORGOT_PASSWORD = True

password_changed # Sent after a user submitted a change password or reset password


˓→form

# - Requires USER_ENABLE_EMAIL = True


# - Requires USER_ENABLE_CHANGE_PASSWORD = True
# - Requires USER_SEND_PASSWORD_CHANGED_EMAIL = True

registered # Sent to users after they submitted a registration form


# - Requires USER_ENABLE_EMAIL = True
# - Requires USER_ENABLE_CONFIRM_EMAIL = False
# - Requires USER_SEND_REGISTERED_EMAIL = True

username_changed # Sent after a user submitted a change username form


# - Requires USER_ENABLE_EMAIL = True
# - Requires USER_ENABLE_CHANGE_USERNAME = True
# - Requires USER_SEND_USERNAME_CHANGED_EMAIL = True

Emails are constructed using three email template files, using the Jinja2 templating engine. The ‘registered’ email, for
example, makes use of the following template files:

registered_subject.txt # Subject line


registered_message.html # Message in HTML format
registered_message.txt # Message in Text format

Which, in turn, depend on the common base template files:

base_subject.txt
base_message.html
base_message.txt

The base templates are shared across all message types. Typically the base templates define the message design (the
look-and-feel) while the message templates define the message content.

7.24. Customizing Emails 65


Flask-User Documentation, Release v1.0

7.24.3 Customizing Email templates

Before we can customize any of the email templates, we’ll need to copy them from the Flask-User install directory to
your application’s template directory.

Copying Email template files

1. Determine the location of where the Flask-User package is installed:

# In a python shell, type the following:


import os
import flask_user
print(os.path.dirname(flask_user.__file__))

# Prints something like:


# ~/.envs/my_app/lib/python3.6/site-packages/flask_user

2. The examples below assume the following:

flask_user dir: ~/.envs/my_app/lib/python3.6/site-packages/flask_user


app dir: ~/dev/my_app

Adjust the examples below to your specific environment.


3. Copy the template files, substituting your flask_user and your template directory accordingly:

# IMPORTANT:
# If you've already worked on customizing form templates, you can (and must) skip
˓→this step,

# since you've already copied the email templates along with the form templates.

cp ~/.envs/my_app/lib/python2.7/site-packages/flask_user/templates/flask_user ~/
˓→dev/my_app/app/templates/.

cp ~/.envs/my_app/lib/python2.7/site-packages/flask_user/templates/flask_user_
˓→base.html ~/dev/my_app/app/templates/.

You should now have an app/template/flask_user directory:

ls -1 ~/dev/my_app/app/templates/flask_user/emails

Expected output:

base_message.html
base_message.txt
base_subject.txt
confirm_email_message.html
confirm_email_message.txt
confirm_email_subject.txt
...

Steps 1) through 3) only need to be performed once.

Editing Email template files

After you’ve copied the Email template files <CopyEmailTemplateFiles>, you can edit any HTML or TXT template
file in your app/templates/flask_user/emails/ directory, and change it to your liking.

66 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

Flask-User makes the following template variables available to all email templates:
app_name # The value of the USER_APP_NAME app config setting
email # The user's email
user # The user's User object
user_manager # The UserManager object

The following email templates also receive the following template variables:
# confirm_email templates
confirm_email_link # Confirm email link with confirm email token

# forgot_password templates
reset_password_link # Reset password link with reset password token

# invite_user templates
accept_invitation_link # Accept invitation like with register token
invited_by_user # The user that created this invitation

Here’s the default base_message.html implementation on github.


Here’s the default confirm_email_message.html implementation on github.

Intro Settings Forms Emails Advanced

7.25 Customizing Forms

Intro Settings Forms Emails Advanced

The following Flask-User forms can be customized:


• AddEmailForm
• ChangeUsernameForm
• EditUserProfileForm
• ForgotPasswordForm
• InviteUserForm
• LoginForm
• RegisterForm
• ResendEmailConfirmationForm
• ResetPasswordForm
For each form, you can customize the following:
• Form Templates
• Customizing Form Classes
• Customizing Form Validators
• Customizing Form Views

7.25. Customizing Forms 67


Flask-User Documentation, Release v1.0

7.25.1 Customizing Form Templates

Before we can customize any of the form templates, we’ll need to copy them from the Flask-User install directory to
your application’s template directory.

Copying Form template files

1. Determine the location of where the Flask-User package is installed:


# In a python shell, type the following:
import os
import flask_user
print(os.path.dirname(flask_user.__file__))

# Prints something like:


# ~/.envs/my_app/lib/python3.6/site-packages/flask_user

2. The examples below assume the following:


flask_user dir: ~/.envs/my_app/lib/python3.6/site-packages/flask_user
app dir: ~/dev/my_app

Adjust the examples below to your specific environment.


3. Copy the form template files, substituting your flask_user and your app/templates directory accordingly:
# IMPORTANT:
# If you've already worked on customizing email templates, you can (and must)
˓→skip this step,

# since you've already copied the form templates along with the email templates.

cp ~/.envs/my_app/lib/python2.7/site-packages/flask_user/templates/flask_user ~/
˓→dev/my_app/app/templates/.

cp ~/.envs/my_app/lib/python2.7/site-packages/flask_user/templates/flask_user_
˓→base.html ~/dev/my_app/app/templates/.

You should now have an app/template/flask_user directory:


ls -1 ~/dev/my_app/app/templates/flask_user

Expected output:
_authorized_base.html
_common_base.html
_macros.html
_public_base.html
change_password.html
change_username.html
emails
...

Steps 1) through 3) only need to be performed once.

Editing Form template files

After you’ve copied the Form template files, you can edit any template file in your app/templates/
flask_user/ directory, and change it to your liking.

68 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

All Flask-User templates extend from app/template/flask_user_layout.html. You can configure all
Flask-User templates to extend from your own layout template by:
editing app/template/flask_user/_common_base.html, and

replacing:
{% extends "flask_user_layout.html" %}

with:
{% extends "layout.html" %}

7.25.2 Customizing Form Classes

Optionally, if you need to add form fields to a Flask-User form, you will need to customize that form like so:
# Customize the User class
class User(db.Model, UserMixin):
...
country = db.Column(db.String(100), nullable=False)

# Customize the Register form:


from flask_user.forms import RegisterForm
class CustomRegisterForm(RegisterForm):
# Add a country field to the Register form
country = StringField(_('Country'), validators=[DataRequired()])

# Customize the User profile form:


from flask_user.forms import UserProfileForm
class CustomUserProfileForm(UserProfileForm):
# Add a country field to the UserProfile form
country = StringField(_('Country'), validators=[DataRequired()])

# Customize Flask-User
class CustomUserManager(UserManager):

def customize(self, app):

# Configure customized forms


self.register_form = CustomRegisterForm
self.user_profile_form = CustomUserProfileForm
# NB: assign: xyz_form = XyzForm -- the class!
# (and not: xyz_form = XyzForm() -- the instance!)

# Setup Flask-User
user_manager = CustomUserManager(app, db, User)

See also:
Forms shows a complete list of customizable forms.
Default forms are defined here

Note: Notice that in a simple use case like this, the form will work without customizing the accompanying view
method. This is because WTForm’s populate_obj() function knows how to move data from form.country.
data to user.country (as long as the property names are identical).

7.25. Customizing Forms 69


Flask-User Documentation, Release v1.0

7.25.3 Customizing Form Validators

Flask user ships with default username and password form field validators that can be customized like so:
from wtforms import ValidationError

# Customize Flask-User
class CustomUserManager(UserManager):

# Override the default password validator


def password_validator(form, field):
if not some_condition:
raise ValidationError('Some error message.')

# Override the default username validator


def password_username(form, field):
if not some_condition:
raise ValidationError('Some error message.')

# Setup Flask-User
user_manager = CustomUserManager(app, db, User)

Examples:
The default password_validator().
The default username_validator().

7.25.4 Customizing Form Views

View methods contain the code that prepares forms (on an HTTP GET) and process forms (on an HTTP POST).
Optionally, if you want to change the default behaviour, you can customize the view methods like so:
# Customize Flask-User
class CustomUserManager(UserManager):

# Override or extend the default login view method


def login_view(self):
...

# Setup Flask-User
user_manager = CustomUserManager(app, db, User)

Warning: View methods perform lots of intricate operations, so use this feature with caution. Be sure to read the
source code of the default view method and make sure you understand all that it does before attempting to modify
its behavior.
Default view methods are defined here

70 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

See also:
UserManager View methods for a complete list of customizable view methods.

Intro Settings Forms Emails Advanced

7.26 Advanced Customizations

Intro Settings Forms Emails Advanced

7.26.1 Customizing the Email, Password and Token Managers

Developers can customize the EmailManager, the PasswordManager, and the TokenManager as follows:
# Customize the EmailManager
from flask_user import EmailManager
class CustomEmailManager(EmailManager):
...

# Customize the PasswordManager


from flask_user import PasswordManager
class CustomPasswordManager(PasswordManager):
...

# Customize the TokenManager


from flask_user import TokenManager
class CustomTokenManager(TokenManager):
...

# Setup Flask-User
user_manager = UserManager(app, db, User)

# Customize Flask-User managers


user_manager.email_manager = CustomEmailManager(app)
user_manager.password_manager = CustomPasswordManager(app, 'bcrypt')
user_manager.token_manager = CustomTokenManager(app)

See also:

EmailManager class,
PasswordManager class, and
TokenManager class.

7.26.2 Implementing a CustomDbAdapter

See Implementing a CustomDbAdapter

7.26. Advanced Customizations 71


Flask-User Documentation, Release v1.0

7.26.3 Implementing a CustomEmailAdapter

See Implementing a CustomEmailAdapter

Intro Settings Forms Emails Advanced

7.27 Porting Basics

Porting Basics Customizations Advanced

7.27.1 Package installs

# Remove unused Python packages


pip uninstall Flask-User # Uninstall v0.6
pip uninstall py-crypt # This may already be absent
pip uninstall Flask-Babel # We're requiring Flask-BabelEx now

# Install new Python packages


pip install Flask-BabelEx # Only if you require internationalization
pip install Flask-User # Install v0.9

Use: pip freeze | grep Flask-User to show the installed Flask-User version, and update your require-
ments.txt file accordingly:

# In requirements.txt:
Flask-User==0.9.{X}

7.27.2 Flask-User setup

We’ve removed the need to specify the type of DbAdapter during application setup.
From v0.6:

from flask_user import UserManager, UserMixin, SQLAlchemyAdapter


...
# Setup Flask-User
db_adapter = SQLAlchemyAdaper(db, User)
user_manager = UserManager(db_adapter, app)

To v0.9+:

from flask_user import UserManager, UserMixin # No SQLAlchemyAdapter here!!


...
# Setup Flask-User
user_manager = UserManager(app, db, User)

Make sure to stop using the legacy SQLAlchemyAdapter or DbAdapter classes as they will trigger legacy warning
exceptions.

72 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

7.27.3 USER_. . . config settings

Some v0.6 USER_... settings have been renamed in v0.9 to better reflect what these settings means. v0.9 still honors
the old v0.6 names, but a deprecation warning message will be printed.
We recommend resolving these warning messages by renaming the following settings:

Replace: USER_ENABLE_RETYPE_PASSWORD
with: USER_REQUIRE_RETYPE_PASSWORD

Replace: USER_ENABLE_LOGIN_WITHOUT_CONFIRM_EMAIL
with: USER_ALLOW_LOGIN_WITHOUT_CONFIRMED_EMAIL

Replace: USER_SHOW_USERNAME_EMAIL_DOES_NOT_EXIST
with: USER_SHOW_EMAIL_DOES_NOT_EXIST
& USER_SHOW_USERNAME_DOES_NOT_EXIST

Replace: MAIL_DEFAULT_SENDER = '"App name" <info@example.com>'


with: USER_EMAIL_SENDER_NAME = 'App name'
& USER_EMAIL_SENDER_EMAIL = info@example.com

7.27.4 User.email_confirmed_at property

We renamed the User.confirmed_at property to User.email_confirmed_at to better reflect what it rep-


resents.
Replace v0.6:

class User(db.Model, UserMixin)


...
confirmed_at = db.Column(db.DateTime())

With v0.9+:

class User(db.Model, UserMixin)


email_confirmed_at = db.Column('confirmed_at', db.DateTime())

Notice how SQLAlchemy allows us to keep using the old confirmed_at column name with the new
email_confirmed_at property.
See Advanced Porting topics for a workaround if you can not rename this property.

7.27.5 Form template customization

No known porting steps are needed for customized .html files.

7.27.6 Email template customization

No known porting steps are needed for customized .html and .txt files.

7.27.7 Contact us

We believe this concludes the Basic Porting steps for for applications with minimal Flask-User customization.

7.27. Porting Basics 73


Flask-User Documentation, Release v1.0

If, after reading Porting Customizations and Advanced Porting topics, you still think this page is incomplete, please
email ling.thio@gmail.com.

Porting Basics Customizations Advanced

7.28 Porting Customizations

Porting Basics Customizations Advanced

Read this if you have:


• Specified custom Form classes (the .py files)
• Specified custom View functions
• Specified custom Password or username validators
• Specified a custom TokenManager
• Used the optional UserAuth class
This topic assumes that you completed the porting tasks described in Porting Basics.

7.28.1 UserManager customization

In v0.6, Flask-User was customized by adding parameters to the UserManager() instantiation. For example:

# Setup Flask-User
db_adapter = SQLAlchemyAdaper(db, User)
user_manager = UserManager(db_adapter, app,
register_form = CustomRegisterForm,
register_view_function = custom_register_view,
password_validator = custom_password_validator,
token_manager = CustomTokenManager())

In v0.9, Flask-User is customized by: - Extending the CustomUserManager class - Setting properties in its
customize() method - Overriding or extending methods

# Customize Flask-User
class CustomUserManager(UserManager):

def customize(self, app):


# Override properties
register_form = CustomRegisterForm()
token_manager = CustomTokenManager(app)

# Override methods
def register_view(self):
...

# Extend methods
def password_validator(self):
super(CustomUserManager, self).password_validator()
...

74 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

In v0.9, almost all UserManager initiation parameters have been obsolted and now only accepts the following:
• Required parameters app, db and UserClass.
• Optional keyword parameters: UserEmailClass and UserInvitationClass.
See also:
UserManager Class

7.28.2 Data-model changes

The email_confirmed_at property has been renamed.


See Porting Basics for porting steps and Advanced Porting topics for an advanced option.

The UserAuth class has been deprecated.


Support for the optional v0.6 UserAuth class has been dropped in v0.9+ to simplify the Flask-User source code and
make it more readable and easier to customize.

See Advanced Porting topics for a workaround if you can not merge the UserAuth and User classes.

The UserInvitation class has been renamed.


The v0.6 UserInvite class has been renamed to UserInvitation in v0.9+ to reflect that it’s an object and not
an action.

Use the following approach to use the new class name while keeping the old table name:

class UserInvitation(db.Model):
__tablename__ = 'user_invite'
...

7.28.3 Password method changes

We changed the verify_password() API


from v0.6 verify_password(password, user)
to v0.9+ verify_password(password, password_hash)
to keep data-model knowledge out of the PasswordManager.

Please change:

user_manager.verify_password(password, user)

into:

verify_password(password, user.password)

7.28. Porting Customizations 75


Flask-User Documentation, Release v1.0

7.28.4 @confirm_email_required decorator deprecated

The @confirm_email_required view decorator has been deprecated for security reasons.
In v0.6, the USER_ENABLE_LOGIN_WITHOUT_CONFIRM_EMAIL setting removed confirmed email protection for
all the views and required developers to re-protect the vulnerable views with @confirm_email_required.
In v0.9+ we adopt the opposite approach where the (renamed) USER_ALLOW_LOGIN_WITHOUT_CONFIRMED_EMAIL=True
setting continues to protect all the views, except those decorated with the new @allow_unconfirmed_email
decorator.

7.28.5 Contact us

We believe this concludes the Porting steps for for applications with a high degree of Flask-User customizations.
If, after reading Advanced Porting topics, you still think this page is incomplete, please email ling.thio@gmail.com.

Porting Basics Customizations Advanced

7.29 Advanced Porting topics

Porting Basics Customizations Advanced

7.29.1 Flask-Login

We recommend NOT upgrading Flask-Login from v0.2.x to v0.3+ unless you need to.
Flask-Login changed user.is_authenticated, user.is_anonymous and user.is_active from being
methods in v0.2 to being properties in v0.3+.
This difference often requires changes in many places (in code and in template files):

user.is_authenticated() --> user.is_authenticated


user.is_anonymous() --> user.is_anonymous
user.is_active() --> user.is_active

7.29.2 TokenManager() changes

The v0.6 TokenManager could only encrypt a single integer ID with less than 16 digits.
The v0.9+ TokenManager can now encrypt a list of items. Each item can be an integer or a string. Integers can be of
any size.
This change enables us to encrypt Mongo Object IDs as well as the last 8 bytes of a password, to invalidate tokens
after their password changed.
As a result, the generated tokens are different, which will affect these areas:
• v0.6 user-session tokens, that were stored in a browser cookie, are no longer valid in v0.9+ and the user will be
required to login again.

76 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

• Unused v0.6 password-reset tokens and user-invitation tokens, are no longer valid in v0.9+ and the affected
users will have to issue new forgot-password emails and new user invitatin emails. This effect is mitigated by
the fact that these tokens are meant to expire relatively quickly.
• user-session tokens and password-reset tokens become invalid if the user changes their password.

7.29.3 Python getters and setters

If you are unable to change property names, you can use Python’s getters and setters to form a bridge between required
property names and actual ones.
Here’s an example of how to map the v0.9 required email_confirmed_at property to your existing
confirmed_at property:

# If the actual property (confirmed_at) name


# differs from required name (email_confirmed_at).
class User(db.Model, UserMixin)
...
# Actual property
confirmed_at = db.Column(db.DateTime())

# Map required property name to actual property


@property
def email_confirmed_at(self):
return self.confirmed_at

@email_confirmed_at.setter
def email_confirmed_at(self, value):
self.confirmed_at = value

7.29.4 Supporting deprecated UserAuth data-models

Because the optional UserAuth and User have a one-to-one relationship to each other, you can use getters and
setters to have Flask-User manage two objects while specifying only the one User class:

# This is your existing UserAuth data-model


# -----------------------------------------
class UserAuth(db.Model):
id = db.Column(db.Integer, primary_key=True)

# Relationship to user
user_id = db.Column(db.Integer(), db.ForeignKey('users.id', ondelete='CASCADE'))
user = db.relationship('User', uselist=False)

# User authentication information


username = db.Column(db.String(50), nullable=False, unique=True)
password = db.Column(db.String(255), nullable=False, server_default='')
email = db.Column(db.String(255), nullable=False, unique=True)
confirmed_at = db.Column(db.DateTime())

# This is your existing User data-model


# -------------------------------------
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)

7.29. Advanced Porting topics 77


Flask-User Documentation, Release v1.0

# User information
active = db.Column('is_active', db.Boolean(), nullable=False, server_default='0')
first_name = db.Column(db.String(100), nullable=False, server_default='')
last_name = db.Column(db.String(100), nullable=False, server_default='')

# One-to-one Relationship
user_auth = db.relationship('UserAuth', uselist=False)

# Create a UserAuth instance when a User instance is created


def __init__(self, *args, **kwargs):
super(User, self).__init__(*args, **kwargs)
self.user_auth = UserAuth(user=self)

# Map required v0.9 properties in the User data-model


# to existing properties in the UserAuth data-model.
# ---------------------------------------------------

# Map the User.username field into the UserAuth.username field


@property
def username(self):
return self.user_auth.username

@username.setter
def username(self, value)
self.user_auth.username = value

# Map the User.password field into the UserAuth.password field


@property
def password(self):
return self.user_auth.password

@password.setter
def password(self, value)
self.user_auth.password = value

# Map the User.email field into the UserAuth.email field


@property
def email(self):
return self.user_auth.email

@email.setter
def email(self, value)
self.user_auth.email = value

# Map the User.email_confirmed_at field into the UserAuth.confirmed_at field


@property
def email_confirmed_at(self):
return self.user_auth.confirmed_at

@email_confirmed_at.setter
def email_confirmed_at(self, value)
self.user_auth.confirmed_at = value

78 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

# Setup Flask-User
user_manager = UserManager(app, db, User)

# -----------------------------------------------------------------
# This code snippet has not yet been tested. You can email
# ling.thio@gmail.com when it works or when you encounter problems.
# When enough people tested this I will remove this comment.
# Thank you!

7.29.5 Logging in without confirmed email addresses

In v0.6, the USER_ENABLE_LOGIN_WITHOUT_CONFIRM_EMAIL=True setting allowed users to login re-


gardless of whether their email addresses were confirmed or not. Sensitive views were protected with an
@confirm_email_required view decorator. This left websites vulnerable to views that were unintentionally
left unprotected.
In v0.9 we renamed the USER_ENABLE_LOGIN_WITHOUT_CONFIRM_EMAIL to
USER_ALLOW_LOGIN_WITHOUT_CONFIRMED_EMAIL to better reflect what this setting does. and we
deprecated the @confirm_email_required view decorator.
In v0.9, we reduced the opportunities for mistakes, by taking the opposite protection approach: Even
with USER_ENABLE_LOGIN_WITHOUT_CONFIRM_EMAIL=True, views with @login_required,
@roles_accepted, and @roles_required decorators continue to be protected against users with-
out confirmed email addresses.
If you want unconfirmed users to access certain views, you will need to add the new
@allow_unconfirmed_email decorator to each view that you choose to expose.

This decorator must follow the @route decorator (as usual)


THis decorator must precede any of the other Flask-Userdecorators

@route(...)
@allow_unconfirmed_email
@login_required
def unprotected_view():
...

Porting Basics Customizations Advanced

7.30 Base templates

templates/base.html
All Flask-User forms extend from the template file tempates/base.h and Flask-User supplies a built-in version
that uses Bootstrap 3.
To make Flask-User use your page template, you will need to create a base.html template file in your application’s
templates directory.
Use {% block content %}{% endblock %} as a placeholder for the forms.

7.30. Base templates 79


Flask-User Documentation, Release v1.0

templates/flask_user/_public_base.html
Public forms are forms that do not require a logged-in user:
• templates/flask_user/forgot_password.html,
• templates/flask_user/login.html,
• templates/flask_user/login_or_register.html,
• templates/flask_user/register.html,
• templates/flask_user/request_email_confirmation.html, and
• templates/flask_user/reset_password.html.
Public forms extend the template file templates/flask_user/_public_base.html, which by default ex-
tends the template file templates/base.html.
If you want the public forms to use a base template file other than templates/base.html, create the
templates/flask_user/_public_base.html file in your application’s templates directory with the
following content:

{% extends 'my_public_base.html' %}

templates/flask_user/_authorized_base.html
Member forms are forms that require a logged-in user:
• templates/flask_user/change_password.html,
• templates/flask_user/change_username.html, and
• templates/flask_user/manage_emails.html.
Member forms extend the template file templates/flask_user/_authorized_base.html, which by de-
fault extends the template file templates/base.html.
If you want the member forms to use a base template file other than templates/base.html, create the
templates/flask_user/_authorized_base.html file in your application’s templates directory with
the following content:

{% extends 'my_authorized_base.html' %}

Summary
The following template files reside in the templates directory:

base.html # root template

flask_user/_authorized_base.html # extends base.html


flask_user/change_password.html # extends flask_user/_authorized_base.html
flask_user/change_username.html # extends flask_user/_authorized_base.html
flask_user/manage_emails.html # extends flask_user/_authorized_base.html

flask_user/_public_base.html # extends base.html


flask_user/forgot_password.html # extends flask_user/_public_base.html
flask_user/login.html # extends flask_user/_public_base.html
flask_user/login_or_register.html # extends flask_user/_public_base.html
flask_user/register.html # extends flask_user/_public_base.html
flask_user/request_email_confirmation.html # extends flask_user/_public_base.html
flask_user/reset_password.html # extends flask_user/_public_base.html

80 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

7.31 Signals (event hooking)

Flask Applications that want to be kept informed about certain actions that took place in the underlying Flask-User
extensions can do so by subscribing ‘signals’ to receive event notification.
Flask-User defines the following signals:

# Signal # Sent when ...


user_changed_password # a user changed their password
user_changed_username # a user changed their username
user_confirmed_email # a user confirmed their email
user_forgot_password # a user submitted a reset password request
user_logged_in # a user logged in
user_logged_out # a user logged out
user_registered # a user registered for a new account
user_reset_password # a user reset their password (forgot password)

See the http://flask.pocoo.org/docs/signals/

7.31.1 Installing Blinker – REQUIRED!

NB: Flask-User relies on Flask signals, which relies on the ‘blinker’ package. Event notification WILL NOT WORK
without first installing the ‘blinker’ package.

pip install blinker

See http://pythonhosted.org/blinker/

7.31.2 Subscribing to Signals

AFTER BLINKER HAS BEEN INSTALLED, An application can receive event notifications by using the event sig-
nal’s connect_via() decorator:

from flask_user import user_logged_in

@user_logged_in.connect_via(app)
def _after_login_hook(sender, user, **extra):
sender.logger.info('user logged in')

For all Flask-User event signals:


- sender points to the app, and
- user points to the user that is associated with this event.

See Subscribing to signals

7.31.3 Troubleshooting

If the code looks right, but the tracking functions are not called, make sure to check to see if the ‘blinker’ package has
been installed (analyze the output of pip freeze).

7.31. Signals (event hooking) 81


Flask-User Documentation, Release v1.0

7.32 Recipes

Here we explain the use of Flask-User through code recipes.

7.32.1 Login Form and Register Form on one page

Some websites may prefer to show the login form and the register form on one page.
Flask-User (v0.4.9 and up) ships with a login_or_register.html form template which requires the following
application config settings:
• USER_LOGIN_TEMPLATE='flask_user/login_or_register.html'
• USER_REGISTER_TEMPLATE='flask_user/login_or_register.html'
This would accomplish the following:
• The /user/login and user/register URLs will now render login_or_register.html.
• login_or_register.html now displays a Login form and a Register form.
• The Login button will post to /user/login
• The Register button will post to /user/register

7.32.2 After registration hook

Some applications require code to execute just after a new user registered for a new account. This can be achieved by
subscribing to the user_registered signal as follows:

from flask_user.signals import user_registered

@user_registered.connect_via(app)
def _after_registration_hook(sender, user, **extra):
sender.logger.info('user registered')

See also: Signals (event hooking)

7.32.3 Hashing Passwords

If you want to populate your database with User records with hashed passwords use user_manager.
hash_password():

user = User(
email='user1@example.com',
password=user_manager.hash_password('Password1'),
)
db.session.add(user)
db.session.commit()

You can verify a password with user_manager.verify_password():

does_match = user_manager.verify_password(password, user.password)

82 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

7.32.4 Account Tracking

Flask-User deliberately stayed away from implementing account tracking features because:
• What to track is often customer specific
• Where to store it is often customer specific
• Custom tracking is easy to implement using signals
Here’s an example of tracking login_count and last_login_ip:

# This code has not been tested

from flask import request


from flask_user.signals import user_logged_in

@user_logged_in.connect_via(app)
def _track_logins(sender, user, **extra):
user.login_count += 1
user.last_login_ip = request.remote_addr
db.session.commit()

7.33 F.A.Q.

Q: Can I see a preview?


A: Yes you can: Flask-User Demo

Q: What are the differences between Flask-User and Flask-Security?


A: The main reason why I wrote Flask-User was because I found it difficult to customize Flask-Security messages and
functionality (in 2013) and because it didn’t offer Username login, multiple emails per user, and Internationalization.

Flask-Security has been around since at least March 2012 and additionally offers Json/AJAX, MongoDB, Peewee, and
Basic HTTP Authentication.
Flask-User has been designed with Full customization in mind and additionally offers Username login and Interna-
tionalization. It exists since December 2013 and contains 661 statements with a 98% test coverage.

Q: Can users login with usernames and email addresses?


A: Yes. Flask-User can be configured to enable usernames, email addresses or both. If both are enabled, users can log
in with either their username or their email address.

Q: Does Flask-User work with existing hashed passwords?


A: Yes. It supports the following:
- passwords hashed by any passlib hashing algorithm (via a config setting)
- passwords hashed by Flask-Security (via a config setting)
- custom password hashes (via custom functions)

Q: What databases does Flask-User support?

7.33. F.A.Q. 83
Flask-User Documentation, Release v1.0

A: Any database that SQLAlchemy supports (via SqlAlchemyAdapter)


and other databases (via custom DBAdapters)

Flask-User shields itself from database operations through a DBAdapter. It ships with a SQLAlchemyAdapter, but the
API is very simple, so other adapters can be easily added. See DbAdapter Interface.

7.34 Flask-User API

• Template variables
• Template functions
• Signals

7.34.1 Template variables

The following template variables are available for use in email and form templates:

user_manager # points to the UserManager object

7.34.2 Template functions

The following template functions are available for use in email and form templates:

# Function Setting # Default


url_for('user.change_password') USER_CHANGE_PASSWORD_URL = '/user/change-
˓→password'

url_for('user.change_username') USER_CHANGE_USERNAME_URL = '/user/change-


˓→username'

url_for('user.confirm_email') USER_CONFIRM_EMAIL_URL = '/user/confirm-


˓→email/<token>'

url_for('user.email_action') USER_EMAIL_ACTION_URL = '/user/email/<id>


˓→/<action>' # v0.5.1 and up
url_for('user.forgot_password') USER_FORGOT_PASSWORD_URL = '/user/forgot-
˓→password'

url_for('user.login') USER_LOGIN_URL = '/user/sign-in'


url_for('user.logout') USER_LOGOUT_URL = '/user/sign-out'
url_for('user.register') USER_REGISTER_URL = '/user/register'
url_for('user.resend_email_confirmation') USER_RESEND_EMAIL_CONFIRMATION_URL = '/user/
˓→resend-email-confirmation' # v0.5.0 and up
url_for('user.reset_password') USER_RESET_PASSWORD_URL = '/user/reset-
˓→password/<token>'

url_for('user.profile') USER_PROFILE_URL = '/user/profile'


˓→ # v0.5.5 and up

hash_password()

user_manager.hash_password(password)
# Returns hashed 'password' using the configured password hash
# Config settings: USER_PASSWORD_HASH_MODE = 'passlib'

84 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

# USER_PASSWORD_HASH = 'bcrypt'
# USER_PASSWORD_SALT = SECRET_KEY

verify_password()

user_manager.verify_password(password, user.password)
# Returns True if 'password' matches the user's 'hashed password'
# Returns False otherwise.

7.34.3 Signals

# Signal # Sent when ...


user_changed_password # a user changed their password
user_changed_username # a user changed their username
user_confirmed_email # a user confirmed their email
user_forgot_password # a user submitted a reset password request
user_logged_in # a user logged in
user_logged_out # a user logged out
user_registered # a user registered for a new account
user_reset_password # a user reset their password (forgot password)

7.35 Misc

7.35.1 Status

Flask-User v1.0 is quite stable and is used in production environments.


It is marked as a Beta release because the API is subject to small changes.
We appreciate it if you would enter issues and enhancement requests into the Flask-User Issue Tracker.

7.35.2 Demo

The Flask-User Demo showcases Flask-User. To protect against spam mis-use, all email features have been disabled.
(If you’re the first visitor in the last hour, it may take a few seconds for Heroku to start this service)

7.35.3 Contact Information

Ling Thio - ling.thio [at] gmail.com


Feeling generous? Tip me on Gittip

7.35.4 Revision History

• v1.0.0 Initial version

7.35. Misc 85
Flask-User Documentation, Release v1.0

7.35.5 Extension Packages

We plan to offer the following Flask-User extensions in the future:


• Flask-User-Profiles: View profile, Edit profile, Upload profile picture
• Flask-User-Social: Login via Google, Facebook and Twitter authentication

7.35.6 Acknowledgements

This project would not be possible without the use of the following amazing offerings:
• Flask
• Flask-BabelEx and Flask-Babel
• Flask-Login
• Flask-Mail
• SQLAlchemy and Flask-SQLAlchemy
• WTForms and Flask-WTF

7.35.7 Contributors

• https://github.com/neurosnap : Register by invitation only


• https://github.com/lilac : Chinese translation
• https://github.com/cranberyxl : Bugfix for login_endpoint & macros.label
• https://github.com/markosys : Early testing and feedback

7.36 Customize

Flask-User has been designed with full customization in mind, and and here is a list of behaviors that can be customized
as needed:
• Emails
• Registration Form
• Labels and Messages
• Form Templates
• View functions
• Password and Username validators
• Password hashing
• Token generation

86 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

7.36.1 Labels and Messages

The following can be customized by editing the English Babel translation file:
• Flash messages (one-time system messages)
• Form field labels
• Validation messages
See Internationalization

7.36.2 Registration Form

We recommend asking for as little information as possible during user registration, and to only prompt new users for
additional information after the registration process has been completed.
Some Websites, however, do want to ask for additional information in the registration form itself.
Flask-User (v0.4.5 and up) has the capability to store extra registration fields in the User or the UserProfile records.
Extra registration fields in the User data-model
Extra fields must be defined in the User data-model:

class User(db.Model, UserMixin):


__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
is_enabled = db.Column(db.Boolean(), nullable=False, default=False)
email = db.Column(db.String(255), nullable=False, default='')
password = db.Column(db.String(255), nullable=False, default='')
# Extra data-model fields
first_name = db.Column(db.String(50), nullable=False, default='')
last_name = db.Column(db.String(50), nullable=False, default='')

def is_active(self):
return self.is_enabled

db_adapter = SQLAlchemyAdapter(db, UserClass=User)

A custom RegisterForm must be defined with field names exactly matching the names of the data-model fields:

class MyRegisterForm(RegisterForm):
first_name = StringField('First name', validators=[DataRequired('First name is
˓→required')])

last_name = StringField('Last name', validators=[DataRequired('Last name is


˓→required')])

user_manager = UserManager(db_adapter, app, register_form=MyRegisterForm)

A custom templates/flask_user/register.html file must be copied and defined with the extra fields. See
Form Templates.
When a new user submits the Register form, Flask-User examines the field names of the form and the User data-model.
For each matching field name, the form field value will be stored in the corresponding User field.
See Github repository; example_apps/register_form_app
Extra registration fields in UserProfile data-model
• Add extra fields to the User data-model

7.36. Customize 87
Flask-User Documentation, Release v1.0

• Extend a custom MyRegisterForm class from the built-in flask_user.forms.RegisterForm class.


• Add extra fields to the form using identical field names.
• Specify your custom registration form: user_manager = UserManager(db_adapter, app,
register_form=MyRegisterForm)
• Copy the built-in templates/flask_user/register.html to your application’s templates/flask_user
directory.
• Add the extra form fields to register.html

7.36.3 Form Templates

Forms are generated using Flask Jinja2 template files. Flask will first look for template files in the application’s
templates directory before looking in Flask-User’s templates directory.
Forms can thus be customized by copying the built-in Form template files from the Flask-User directory to your
application’s directory and editing the new copy.
Flask-User typically installs in the flask_user sub-directory of the Python packages directory. The location of this
directory depends on Python, virtualenv and pip and can be determined with the following command:
python -c "from distutils.sysconfig import get_python_lib; print get_python_lib();"

Let’s assume that:


• The Python packages dir is: ~/.virtualenvs/ENVNAME/lib/python2.7/site-packages/
• The Flask-User dir is: ~/.virtualenvs/ENVNAME/lib/python2.7/site-packages/
flask_user/
• Your app directory is: ~/path/to/YOURAPP/YOURAPP (your application directory typically contains the
‘static’ and ‘templates’ sub-directories).
Forms can be customized by copying the form template files like so:
cd ~/path/to/YOURAPP/YOURAPP
mkdir -p templates/flask_user
cp ~/.virtualenvs/ENVNAME/lib/python2.7/site-packages/flask_user/templates/flask_user/
˓→*.html templates/flask_user/.

and by editing the copies to your liking.


The following form template files resides in the templates directory and can be customized:
base.html # root template

flask_user/_authorized_base.html # extends base.html


flask_user/change_password.html # extends flask_user/_authorized_base.html
flask_user/change_username.html # extends flask_user/_authorized_base.html
flask_user/manage_emails.html # extends flask_user/_authorized_base.html
flask_user/edit_user_profile.html # extends flask_user/_authorized_base.html

flask_user/_public_base.html # extends base.html


flask_user/forgot_password.html # extends flask_user/_public_base.html
flask_user/login.html # extends flask_user/_public_base.html
flask_user/login_or_register.html # extends flask_user/_public_base.html
flask_user/register.html # extends flask_user/_public_base.html
flask_user/request_email_confirmation.html # extends flask_user/_public_base.html
flask_user/reset_password.html # extends flask_user/_public_base.html

88 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

If you’d like the Login form and the Register form to appear on one page, you can use the following application config
settings:

# Place the Login form and the Register form on one page:
# Only works for Flask-User v0.4.9 and up
USER_LOGIN_TEMPLATE = 'flask_user/login_or_register.html'
USER_REGISTER_TEMPLATE = 'flask_user/login_or_register.html'

7.36.4 Password and Username Validators

Flask-User comes standard with a password validator (at least 6 chars, 1 upper case let-
ter, 1 lower case letter, 1 digit) and with a username validator (at least 3 characters in
“abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._”).
Custom validators can be specified by setting an property on the Flask-User’s UserManager object:

from wtforms.validators import ValidationError

def my_password_validator(form, field):


password = field.data
if len(password) < 8:
raise ValidationError(_('Password must have at least 8 characters'))

def my_username_validator(form, field):


username = field.data
if len(username) < 4:
raise ValidationError(_('Username must be at least 4 characters long'))
if not username.isalnum():
raise ValidationError(_('Username may only contain letters and numbers'))

user_manager = UserManager(db_adapter,
password_validator=my_password_validator,
username_validator=my_username_validator)
user_manager.init_app(app)

7.36.5 Password hashing

To hash a password, Flask-User:


• calls user_manager.hash_password(),
• which calls user_manager.password_crypt_context,
• which is initialized to CryptContext(schemes=[app.config['USER_PASSWORD_HASH']]),
• where USER_PASSWORD_HASH = 'bcrypt'.
See http://pythonhosted.org/passlib/new_app_quickstart.html
Developers can customize the password hashing in the following ways:
By changing an application config setting:

USER_PASSWORD_HASH = 'sha512_crypt'

By changing the crypt_context:

7.36. Customize 89
Flask-User Documentation, Release v1.0

my_password_crypt_context = CryptContext(
schemes=['bcrypt', 'sha512_crypt', 'pbkdf2_sha512'])
user_manager = UserManager(db_adapter, app,
password_crypt_context=my_password_crypt_context)

By sub-classing hash_password():

class MyUserManager(UserManager):
def hash_password(self, password):
return self.password

def verify_password(self, password, password_hash)


return self.hash_password(password) == password_hash

Backward compatibility with Flask-Security


Flask-Security performs a SHA512 HMAC prior to calling passlib. To continue using passwords that have been
generated with Flask-Security, add the following settings to your application config:

# Keep the following Flaks and Flask-Security settings the same


SECRET_KEY = ...
SECURITY_PASSWORD_HASH = ...
SECURITY_PASSWORD_SALT = ...

# Set Flask-Security backward compatibility mode


USER_PASSWORD_HASH_MODE = 'Flask-Security'
USER_PASSWORD_HASH = SECURITY_PASSWORD_HASH
USER_PASSWORD_SALT = SECURITY_PASSWORD_SALT

7.36.6 View Functions

The built-in View Functions contain considerable business logic, so we recommend first trying the approach of Form
Templates before making use of customized View Functions.
Custom view functions are specified by setting an property on the Flask-User’s UserManager object:

# View functions
user_manager = UserManager(db_adapter,
change_password_view_function = my_view_function1,
change_username_view_function = my_view_function2,
confirm_email_view_function = my_view_function3,
email_action_view_function = my_view_function4,
forgot_password_view_function = my_view_function5,
login_view_function = my_view_function6,
logout_view_function = my_view_function7,
manage_emails_view_function = my_view_function8,
register_view_function = my_view_function9,
resend_email_confirmation_view_function = my_view_function10,
reset_password_view_function = my_view_function11,
)
user_manager.init_app(app)

7.36.7 Token Generation

To be documented.

90 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

7.36.8 Emails

Emails are generated using Flask Jinja2 template files. Flask will first look for template files in the application’s
templates directory before looking in Flask-User’s templates directory.
Emails can thus be customized by copying the built-in Email template files from the Flask-User directory to your
application’s directory and editing the new copy.
Flask-User typically installs in the flask_user sub-directory of the Python packages directory. The location of this
directory depends on Python, virtualenv and pip and can be determined with the following command:

python -c "from distutils.sysconfig import get_python_lib; print get_python_lib();"

Let’s assume that:


• The Python packages dir is: ~/.virtualenvs/ENVNAME/lib/python2.7/site-packages/
• The Flask-User dir is: ~/.virtualenvs/ENVNAME/lib/python2.7/site-packages/
flask_user/
• Your app directory is: ~/path/to/YOURAPP/YOURAPP (your application directory typically contains the
‘static’ and ‘templates’ sub-directories).
The built-in Email template files can be copied like so:

cd ~/path/to/YOURAPP/YOURAPP
mkdir -p templates/flask_user/emails
cp ~/.virtualenvs/ENVNAME/lib/python2.7/site-packages/flask_user/templates/flask_user/
˓→emails/* templates/flask_user/emails/.

Each email type has three email template files. The ‘registered’ email for example has the following files:

templates/flask_user/emails/registered_subject.txt # The email subject line


templates/flask_user/emails/registered_message.html # The email message in HTML
˓→format

templates/flask_user/emails/registered_message.txt # The email message in Text


˓→format

Each file is extended from the base template file:

templates/flask_user/emails/base_subject.txt
templates/flask_user/emails/base_message.html
templates/flask_user/emails/base_message.txt

The base template files are used to define email elements that are similar in all types of email messages.

If, for example, for every email you want to:


- Set the background color and padding,
- Start with a logo and salutation, and
- End with a signature,
you can define templates/flask_user/emails/base_message.html like so

<div style="background-color: #f4f2dd; padding: 10px;">


<p><img src="http://example.com/static/images/email-logo.png"></p>
<p>Dear Customer,</p>
{% block message %}{% endblock %}

7.36. Customize 91
Flask-User Documentation, Release v1.0

<p>Sincerely,<br/>
The Flask-User Team</p>
</div>

and define the confirmation specific messages in templates/flask_user/emails/


confirm_email_message.html like so:

{% extends "flask_user/emails/base_message.html" %}

{% block message %}
<p>Thank you for registering with Flask-User.</p>
<p>Visit the link below to complete your registration:</p>
<p><a href="{{ confirm_email_link }}">Confirm your email address</a>.</p>
<p>If you did not initiate this registration, you may safely ignore this email.</p>
{% endblock %}

7.36.9 SQLDbAdapter

Flask-User uses SQLDbAdapter and installs Flask-SQLAlchemy by default. No customization is required to work
with SQL databases.
Configure the SQLALCHEMY_DATABASE_URI setting in your app config to point to the desired server and database.

7.36.10 MongoDbAdapter

Flask-User ships with a MongoDbAdapter, but Flask-MongoEngine must be installed manually:

pip install Flask-MongeEngine

and minor customization is required to use and configure the MongoDbAdapter:

# Setup Flask-MongoEngine
from Flask-MongoEngine import MongoEngine
db = MongoEngine(app)

# Customize Flask-User
class CustomUserManager(UserManager):

def customize(self, app):


# Use the provided MongoDbAdapter
from flask_user.db_adapters import MongoDbAdapter
self.db_adapter = MongoDbAdapter(app, db)

# Define the User document


# NB: Make sure to add flask_user UserMixin !!!
class User(db.Document, UserMixin):

# User authentication information


username = db.StringField(default='')
email = db.StringField(default='')
password = db.StringField()
email_confirmed_at = db.DateTimeField(default=None)

92 Chapter 7. Table of Contents


Flask-User Documentation, Release v1.0

# User information
first_name = db.StringField(default='')
last_name = db.StringField(default='')

# Relationships
roles = db.ListField(db.StringField(), default=[])

# Setup Flask-User
user_manager = CustomUserManager(app, db, User)

Configure the MONGODB_SETTINGS setting in your app config to point to the desired server and database.

7.36.11 SMTPEmailAdapter

Flask-User uses the SMTPEmailAdapter and install Flask-Mail by default. No customization is required to use SMT-
PEmailAdapter to send emails via SMTP.
Configure the MAIL_... settings in your app config to point to the desired SMTP server and account.

7.36.12 SendmailEmailAdapter

Flask-User ships with a SendmailEmailAdapter, but Flask-Sendmail must be installed manually:

pip install Flask-Sendmail

and minor customization is required use to SendmailEmailAdapter to send emails via sendmail.:

# Customize Flask-User
class CustomUserManager(UserManager):

def customize(self, app):


# Use the provided SendmailEmailAdapter
from flask_user.email_adapters import SendmailEmailAdapter
self.email_adapter = SendmailEmailAdapter(app)

# Setup Flask-User
user_manager = CustomUserManager(app, db, User)

No configuration is required (other than setting up sendmail on your system).

7.36.13 SendgridEmailAdapter

Flask-User ships with a SendgridEmailAdapter, but sendgrid-python needs to be installed manually:

pip install sendgrid

and minor customization is required to use SendgridEmailAdapter to send emais via SendGrid:

7.36. Customize 93
Flask-User Documentation, Release v1.0

# Customize Flask-User
class CustomUserManager(UserManager):

def customize(self, app):


# Use the provided SendgridEmailAdapter
from flask_user.email_adapters import SendgridEmailAdapter
self.email_adapter = SendgridEmailAdapter(app)

# Setup Flask-User
user_manager = CustomUserManager(app, db, User)

Configuration: TBD.

94 Chapter 7. Table of Contents


Index

Symbols drop_all_tables() (DbAdapterInterface method), 47


__init__() (DbAdapterInterface method), 46 drop_all_tables() (DBManager method), 30
__init__() (EmailAdapterInterface method), 49
E
A EditUserProfileForm (class in flask_user.forms), 50
email_action_view() (UserManager__Views method), 59
add_object() (DbAdapterInterface method), 46
email_is_available() (UserManager__Utils method), 58
add_user() (DBManager method), 29
EmailAdapterInterface (class in
add_user_email() (DBManager method), 29
flask_user.email_adapters.email_adapter_interface),
add_user_invitation() (DBManager method), 29
49
add_user_role() (DBManager method), 29
EmailManager (class in flask_user.email_manager), 31
AddEmailForm (class in flask_user.forms), 50
encode_data_items() (TokenManager method), 33
allow_unconfirmed_email() (in module
encode_int() (TokenManager method), 34
flask_user.decorators), 48
encrypt_string() (TokenManager method), 33
C F
call_or_get() (UserManager__Utils method), 57 find_first_object() (DbAdapterInterface method), 46
change_password_view() (UserManager__Views find_objects() (DbAdapterInterface method), 46
method), 59 find_user_by_username() (DBManager method), 29
change_username_view() (UserManager__Views find_user_emails() (DBManager method), 29
method), 59 forgot_password_view() (UserManager__Views method),
ChangeUsernameForm (class in flask_user.forms), 50 59
commit() (DbAdapterInterface method), 46 ForgotPasswordForm (class in flask_user.forms), 51
commit() (DBManager method), 29
confirm_email_view() (UserManager__Views method), G
59 generate_token() (TokenManager method), 32
create_all_tables() (DbAdapterInterface method), 47 generate_token() (UserManager__Utils method), 58
create_all_tables() (DBManager method), 30 get_object() (DbAdapterInterface method), 46
customize() (UserManager method), 52 get_primary_user_email() (DBManager method), 29
get_user_and_user_email_by_email() (DBManager
D method), 30
DbAdapterInterface (class in get_user_and_user_email_by_id() (DBManager method),
flask_user.db_adapters.db_adapter_interface), 30
46 get_user_by_id() (DBManager method), 30
DBManager (class in flask_user.db_manager), 29 get_user_email_by_id() (DBManager method), 30
decode_data_items() (TokenManager method), 33 get_user_invitation_by_id() (DBManager method), 30
decode_int() (TokenManager method), 34 get_user_roles() (DBManager method), 30
decrypt_string() (TokenManager method), 33
delete_object() (DbAdapterInterface method), 46 H
delete_object() (DBManager method), 29 hash_password() (PasswordManager method), 31

95
Flask-User Documentation, Release v1.0

hash_password() (UserManager__Utils method), 58 U


unauthenticated_view() (UserManager__Views method),
I 59
invite_user_view() (UserManager__Views method), 59 unauthorized_view() (UserManager__Views method), 59
InviteUserForm (class in flask_user.forms), 51 USER_AFTER_CHANGE_PASSWORD_ENDPOINT
(UserManager__Settings attribute), 57
L USER_AFTER_CHANGE_USERNAME_ENDPOINT
login_required() (in module flask_user.decorators), 47 (UserManager__Settings attribute), 57
login_view() (UserManager__Views method), 59 USER_AFTER_CONFIRM_ENDPOINT (UserMan-
LoginForm (class in flask_user.forms), 51 ager__Settings attribute), 57
logout_view() (UserManager__Views method), 59 USER_AFTER_EDIT_USER_PROFILE_ENDPOINT
(UserManager__Settings attribute), 57
M USER_AFTER_FORGOT_PASSWORD_ENDPOINT
make_safe_url() (UserManager__Utils method), 58 (UserManager__Settings attribute), 57
USER_AFTER_INVITE_ENDPOINT (UserMan-
P ager__Settings attribute), 57
password_validator() (UserManager method), 52 USER_AFTER_LOGIN_ENDPOINT (UserMan-
PasswordManager (class in ager__Settings attribute), 57
flask_user.password_manager), 31 USER_AFTER_LOGOUT_ENDPOINT (UserMan-
prepare_domain_translations() (UserManager__Utils ager__Settings attribute), 57
method), 58 USER_AFTER_REGISTER_ENDPOINT (UserMan-
ager__Settings attribute), 57
R USER_AFTER_RESEND_EMAIL_CONFIRMATION_ENDPOINT
register_view() (UserManager__Views method), 59 (UserManager__Settings attribute), 57
RegisterForm (class in flask_user.forms), 51 USER_AFTER_RESET_PASSWORD_ENDPOINT
resend_email_confirmation_view() (UserMan- (UserManager__Settings attribute), 57
ager__Views method), 59 USER_ALLOW_LOGIN_WITHOUT_CONFIRMED_EMAIL
ResendEmailConfirmationForm (class in (UserManager__Settings attribute), 54
flask_user.forms), 51 USER_APP_NAME (UserManager__Settings attribute),
reset_password_view() (UserManager__Views method), 54
59 USER_AUTO_LOGIN (UserManager__Settings at-
ResetPasswordForm (class in flask_user.forms), 51 tribute), 54
roles_accepted() (in module flask_user.decorators), 48 USER_AUTO_LOGIN_AFTER_CONFIRM (UserMan-
roles_required() (in module flask_user.decorators), 48 ager__Settings attribute), 54
USER_AUTO_LOGIN_AFTER_REGISTER (UserMan-
S ager__Settings attribute), 54
save_object() (DbAdapterInterface method), 46 USER_AUTO_LOGIN_AFTER_RESET_PASSWORD
save_object() (DBManager method), 30 (UserManager__Settings attribute), 54
save_user_and_user_email() (DBManager method), 30 USER_AUTO_LOGIN_AT_LOGIN (UserMan-
send_confirm_email_email() (EmailManager method), ager__Settings attribute), 54
31 USER_CHANGE_PASSWORD_TEMPLATE (User-
send_email_message() (EmailAdapterInterface method), Manager__Settings attribute), 56
49 USER_CHANGE_PASSWORD_URL (UserMan-
send_invite_user_email() (EmailManager method), 31 ager__Settings attribute), 56
send_password_changed_email() (EmailManager USER_CHANGE_USERNAME_TEMPLATE (User-
method), 31 Manager__Settings attribute), 56
send_registered_email() (EmailManager method), 31 USER_CHANGE_USERNAME_URL (UserMan-
send_reset_password_email() (EmailManager method), ager__Settings attribute), 56
31 USER_CONFIRM_EMAIL_EXPIRATION (UserMan-
send_username_changed_email() (EmailManager ager__Settings attribute), 55
method), 31 USER_CONFIRM_EMAIL_TEMPLATE (UserMan-
ager__Settings attribute), 56
T USER_CONFIRM_EMAIL_URL (UserMan-
TokenManager (class in flask_user.token_manager), 32 ager__Settings attribute), 56

96 Index
Flask-User Documentation, Release v1.0

USER_EDIT_USER_PROFILE_TEMPLATE (User- (UserManager__Settings attribute), 56


Manager__Settings attribute), 56 USER_PASSLIB_CRYPTCONTEXT_SCHEMES
USER_EDIT_USER_PROFILE_URL (UserMan- (UserManager__Settings attribute), 55
ager__Settings attribute), 56 USER_PASSWORD_CHANGED_EMAIL_TEMPLATE
USER_EMAIL_ACTION_URL (UserMan- (UserManager__Settings attribute), 57
ager__Settings attribute), 56 USER_REGISTER_TEMPLATE (UserMan-
USER_EMAIL_SENDER_EMAIL (UserMan- ager__Settings attribute), 56
ager__Settings attribute), 54 USER_REGISTER_URL (UserManager__Settings at-
USER_EMAIL_SENDER_NAME (UserMan- tribute), 56
ager__Settings attribute), 54 USER_REGISTERED_EMAIL_TEMPLATE (User-
USER_ENABLE_CHANGE_PASSWORD (UserMan- Manager__Settings attribute), 57
ager__Settings attribute), 53 USER_REQUIRE_INVITATION (UserMan-
USER_ENABLE_CHANGE_USERNAME (UserMan- ager__Settings attribute), 54
ager__Settings attribute), 53 USER_REQUIRE_RETYPE_PASSWORD (UserMan-
USER_ENABLE_CONFIRM_EMAIL (UserMan- ager__Settings attribute), 55
ager__Settings attribute), 53 USER_RESEND_CONFIRM_EMAIL_TEMPLATE
USER_ENABLE_EMAIL (UserManager__Settings at- (UserManager__Settings attribute), 56
tribute), 53 USER_RESEND_EMAIL_CONFIRMATION_URL
USER_ENABLE_FORGOT_PASSWORD (UserMan- (UserManager__Settings attribute), 56
ager__Settings attribute), 53 USER_RESET_PASSWORD_EMAIL_TEMPLATE
USER_ENABLE_INVITE_USER (UserMan- (UserManager__Settings attribute), 57
ager__Settings attribute), 53 USER_RESET_PASSWORD_EXPIRATION (UserMan-
USER_ENABLE_MULTIPLE_EMAILS (UserMan- ager__Settings attribute), 55
ager__Settings attribute), 53 USER_RESET_PASSWORD_TEMPLATE (UserMan-
USER_ENABLE_REGISTER (UserManager__Settings ager__Settings attribute), 56
attribute), 53 USER_RESET_PASSWORD_URL (UserMan-
USER_ENABLE_REMEMBER_ME (UserMan- ager__Settings attribute), 56
ager__Settings attribute), 53 USER_SEND_PASSWORD_CHANGED_EMAIL
USER_ENABLE_USERNAME (UserMan- (UserManager__Settings attribute), 54
ager__Settings attribute), 53 USER_SEND_REGISTERED_EMAIL (UserMan-
USER_FORGOT_PASSWORD_TEMPLATE (User- ager__Settings attribute), 54
Manager__Settings attribute), 56 USER_SEND_USERNAME_CHANGED_EMAIL
USER_FORGOT_PASSWORD_URL (UserMan- (UserManager__Settings attribute), 54
ager__Settings attribute), 56 USER_SHOW_EMAIL_DOES_NOT_EXIST (User-
user_has_confirmed_email() (DBManager method), 30 Manager__Settings attribute), 55
USER_INVITE_EXPIRATION (UserManager__Settings USER_SHOW_USERNAME_DOES_NOT_EXIST
attribute), 55 (UserManager__Settings attribute), 55
USER_INVITE_USER_EMAIL_TEMPLATE (User- USER_UNAUTHENTICATED_ENDPOINT (UserMan-
Manager__Settings attribute), 57 ager__Settings attribute), 57
USER_INVITE_USER_TEMPLATE (UserMan- USER_UNAUTHORIZED_ENDPOINT (UserMan-
ager__Settings attribute), 56 ager__Settings attribute), 57
USER_INVITE_USER_URL (UserManager__Settings USER_USER_SESSION_EXPIRATION (UserMan-
attribute), 56 ager__Settings attribute), 55
USER_LOGIN_TEMPLATE (UserManager__Settings USER_USERNAME_CHANGED_EMAIL_TEMPLATE
attribute), 56 (UserManager__Settings attribute), 57
USER_LOGIN_URL (UserManager__Settings attribute), UserManager (class in flask_user.user_manager), 51
56 UserManager__Settings (class in
USER_LOGOUT_URL (UserManager__Settings at- flask_user.user_manager__settings), 53
tribute), 56 UserManager__Utils (class in
USER_MANAGE_EMAILS_TEMPLATE (UserMan- flask_user.user_manager__utils), 57
ager__Settings attribute), 56 UserManager__Views (class in
USER_MANAGE_EMAILS_URL (UserMan- flask_user.user_manager__views), 58
ager__Settings attribute), 56 username_is_available() (DBManager method), 30
USER_PASSLIB_CRYPTCONTEXT_KEYWORDS username_validator() (UserManager method), 52

Index 97
Flask-User Documentation, Release v1.0

V
verify_password() (PasswordManager method), 32
verify_password() (UserManager__Utils method), 58
verify_token() (TokenManager method), 33
verify_token() (UserManager__Utils method), 58

98 Index

You might also like