User Login
Authenticating users
Setting up Flask-Login
This plugin will make it super easy
for us to log users in and out.
Install the flask-login plugin
pip install flask-login
Python plugins need to be
installed from the shell.
Ready for use
We can now use it by importing the
flask-login package wherever we need.
Create a usermanager file
In your website’s public folder,
create a new file usermanager.py
Import your website
from public import website
Import your message-board Flask website
at the top of your usermanager.py file.
Import flask login
from public import website
from flask.ext import login as flask_login
Also import the flask-login plugin
so we can use the additional functionality.
Add a secret key
from public import website
from flask.ext import login as flask_login
website.secret_key = 'super secret string'
This is a requirement of flask-login
which we need to avoid errors.
Initialise flask login
from public import website
from flask.ext import login as flask_login
website.secret_key = 'super secret string'
login_manager = flask_login.LoginManager()
login_manager.init_app(website)
Create an instance of the login manager
and pass it a reference to your web app.
Check website works
Open your website in the browser and
click through pages to check they still work.
Your site should work, with nothing changed.
Create a User class
The flask-login plugin requires that we have
a User class with a set of predefined functions.
Create a User class
login_manager = flask_login.LoginManager()
login_manager.init_app(website)
class User(flask_login.UserMixin):
def __init__(self, username, password):
self.username = username
self.password = password
self.authenticated = False
In your user manager after the login manager setup code,
create a User class which requires a username and password.
Add a function to get the id
class User(flask_login.UserMixin):
def __init__(self, username, password):
self.username = username
self.password = password
self.authenticated = False
def get_id(self):
return self.username
The flask-login plugin needs to be able to
access a unique id for every user.
Add a function to check authentication
def __init__(self, username, password):
self.username = username
self.password = password
self.authenticated = False
def get_id(self):
return self.username
def is_authenticated(self):
return self.authenticated
In our app, if we find a user in the database
we assume they are authenticated so always return True.
Add a function to check if a user is active
def __init__(self, username, password):
self.username = username
self.password = password
self.authenticated = False
def get_id(self):
return self.username
def is_authenticated(self):
return self.authenticated
def is_active(self):
return True
In our app, every user who exists can be treated
as an “active” user, so always return true.
Add a function to check if a user is anonymous
def get_id(self):
return self.username
def is_authenticated(self):
return self.authenticated
def is_active(self):
return True
def is_anonymous(self):
return False
Our app doesn’t support anonymous users,
so this function always returns False.
Check your User class structure is correct
class User(flask_login.UserMixin):
def __init__(self, username, password):
self.username = username
self.password = password
self.authenticated = False
def get_id(self):
return self.username
def is_authenticated(self):
return self.authenticated
def is_active(self):
return True
def is_anonymous(self):
return False
Your finished User class should look like this.
Create a user loader function
The flask-login plugin also requires us to have a
function which can load a user based on their username.
Create a dummy user loader function
@login_manager.user_loader
def load_user(username):
return None
In your usermanager file, create a placeholder
function called load_user which returns None.
Site should work
Open your site in the browser and check
that everything is still working.
Your site should work, but still no change!
Restrict access to pages
We can make certain pages of our site
only accessible for logged in users.
Import the “login required” mixin
from flask.ext.login import login_required
In the usermanager import login_required,
a mixin we can apply to routes to restrict access.
Import the user manager into routes
from public import usermanager
This will allow us to use the login_required mixin
and other functions we defined in the usermanager.
Add “login required” to the new message page
@website.route('/new-message')
@usermanager.login_required
def new_message():
return render_template('new-message.html')
This tells Flask that login is required
to access the new_message route.
Check access is restricted
Navigate to the new-message page in your browser
to check that access is restricted.
You should see an “Unauthorized” notice.
Set the login page
login_manager = flask_login.LoginManager()
login_manager.init_app(website)
login_manager.login_view = "sign_in"
This tells the flask-login plugin where people
should be sent if they are not logged in and
try to access a restricted page.
Check the login redirect works
Try to navigate to the restricted “new message” page
and check that you are redirected to the login page.
Restricted pages should redirect to the login page.
Create function to sign in users
This function in usermanager will take a
username and password, then return a signed in user.
Add the “sign_in_user” function to usermanager
def sign_in_user(username, password):
user = load_user(username)
if user and user.password == password:
user.authenticated = True
flask_login.login_user(user)
return flask_login.current_user
This function creates an actual User object
and logs that user in using flask-login.
Check your app runs
Run your website and click around
to check for any new errors.
Your website should run and be error-free.
Authenticate the user
When the user signs in, we need to process the
username and password they entered in the form.
Use the username and password to authenticate
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
user = usermanager.sign_in_user(username, password)
Modify your sign-in route to pass the username
and password to the sign_in_user
function.
Redirect based on the result
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
user = usermanager.sign_in_user(username, password)
if user.is_authenticated():
return redirect('/')
else:
return render_template('sign-in.html')
If the user exists, they will be authenticated
and we can redirect them to the home page.
If they don’t exist, we keep them on the sign in page.
Import the redirect package
from flask import redirect
Import the redirect package
at the top of routes.py
Check your site works
Everything should work as normal, except that
signing in will always keep you on the sign in page.
Implement the user loader function
We have a function to load a user but it
always returns None, it should return a user.
Import the datamanager into the usermanager
from public import datamanager
Import the data manager so that we can
access the database to look up a user.
Write a query to find the user
@login_manager.user_loader
def load_user(username):
query_string = (
'SELECT user_id, username, password, first_name, last_name '
'FROM users '
'WHERE username = ?'
)
The query is fairly straightforward, but we also
included a question mark so we can pass in a username.
Run the query, passing it the username parameter
@login_manager.user_loader
def load_user(username):
query_string = (
'SELECT user_id, username, password, first_name, last_name '
'FROM users '
'WHERE username = ?'
)
query_result = datamanager.query_db(query_string, [username], one=True)
If the query fails then return None
@login_manager.user_loader
def load_user(username):
query_string = (
'SELECT user_id, username, password, first_name, last_name '
'FROM users '
'WHERE username = ?'
)
query_result = datamanager.query_db(query_string, [username], one=True)
if query_result == None:
return None
If the query succeeds then create and return a User
query_result = datamanager.query_db(query_string, [username], one=True)
if query_result == None:
return None
else:
user = User(
query_result['username'],
query_result['password']
)
return user
Attach the additional optional user info
query_result = datamanager.query_db(query_string, [username], one=True)
if query_result == None:
return None
else:
user = User(
query_result['username'],
query_result['password']
)
user.user_id = query_result['user_id']
user.first_name = query_result['first_name']
user.last_name = query_result['last_name']
return user
We can also store the user’s user_id
and their first and last name on the User.
Site should be working
Check your site is working
and sign in is supported.
You should be able to log in to your website.
Signing in should allow access to new messages
Check that once you’ve signed in
you can access the new message page.
Sign out user
Now we can also add functionality
for a user to sign out.
Create sign out route
# sign out page
@website.route('/sign-out')
def sign_out():
return render_template('sign-out.html')
Create sign_out_user function
In usermanager.py:
def sign_out_user():
flask_login.logout_user()
Call sign out function from route
# sign out page
@website.route('/sign-out')
def sign_out():
usermanager.sign_out_user()
return render_template('sign-out.html')
Test that you can sign in and sign out
Password encryption
You should totes encrypt passwords
but we’re not doing it today
Using user data in templates
We can display user data, or use their info
to show and hide elements on a page.
Displaying a username
{% if current_user.is_authenticated %}
Hello {{ current_user.username }}
{% endif %}
Smart sign in / sign out links
<nav>
<a href="/">Home</a>
<a href="/new-message">New Message</a>
{% if current_user.is_authenticated %}
<a href="/sign-out">Sign out</a>
{% else %}
<a href="/sign-in">Sign in</a>
{% endif %}
</nav>
User Login: Complete!
Loading...