Kensuke Kousaka's Blog

Notes for Developing Software, Service.

Note about Flask: How to implement authentication system on Flask

This article describes how to implement user authentication system to Flask. Base program that this article uses is created in previously posted article.

Edit app.py

Edit app.py by following.

# -*- coding: utf-8 -*-

from flask import Flask, render_template
from flask import request, jsonify
import json
from flask import session, redirect, url_for
import os

app = Flask(__name__)
# secret key that use to encrypt cookie
app.config['SECRET_KEY'] = os.urandom(24)


# operation before each route method
@app.before_request
def before_request():
    # not check for accessing to static files
    if request.path.startswith('/static/'):
        return
    # session contains username, so already logged in
    if session.get('username') is not None:
        return
    # request related to login page
    if request.path == '/login':
        return
    # not logged in, and request is not related to login page
    return redirect('/login')


# operate login
@app.route('/login', methods=['GET', 'POST'])
def login():
    # login
    if request.method == 'POST' and _is_account_valid():
        # save user name to session, then redirect to top page
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    # return to login page
    return render_template('login.html')


# authneticate user
def _is_account_valid():
    username = request.form.get('username')
    # This example, account is valid when username equals to 'admin'
    if username == 'admin':
        return True
    return False


# operate logout
@app.route('/logout')
def logout():
    # remove username from session
    session.pop('username', None)
    return redirect(url_for('login'))


@app.route('/')
def index():
    return render_template('index.html')


@approute('/postText', methods=['POST'])
def lower_conversion():
    text = request.json['text']
    if 'ping' in text:
        return_data = {'result':'pong'}
        return jsonify(ResultSet=json.dumps(return_data))
    lower_text = text.lower()
    return_data = {'result':lower_text}
    return jsonify(ResultSet=json.dumps(return_data))


if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8080)

Import session, redirect, url_for from flask, and import os for generating randam value. app.config['SECRET_KEY'] = os.urandom(24) is code that generate and save secret key to use for encrypting cookie that save session information. This example specify 24 bytes for length of random value. It require enough length for security reason (so it have not to specify small value).

before_request() checks whole requests whether user is currently logged in or not before processing requests. If user is currently not logged in, redirect to login page and require user to login. login() and _is_account_valid() are login related methods. Especially _is_account_valid() is in charge of user authentication. This example implements that succeed authentication when user name is equal to 'admin', but actual system require more secure implementation, needless to say.

Logout process operate in logout().

Create login.html

Create templates/login.html by following.

<!DOCTYPE html>
<html lang=ja>
 <head>
   <meta charset="utf-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1">

   <title>HelloFlask</title>

   <!-- Bootstrap CDN -->
   <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
   <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
 </head>
 <body>
   {% if error %}
     {{ error }}
   {% endif %}
   <nav class="navbar navbar-inverse">
     <div class="container-fluid">
       <div class="navbar-header">
         <a class="navbar-brand">HelloFlask</a>
       </div> <!-- navbar-header -->
       <ul class="nav navbar-nav navbar-right">
         <li><a href="#signinModal" data-toggle="modal">Sign in</a></li>
       </ul>
     </div> <!-- container-fluid -->
   </nav>
   <div class="container-fluid">
     <div class="row">
       <div class="col-sm-12 col-md-12">
         <h1 class="text-center">HelloFlask</h1>
       </div> <!-- mainform -->
     </div> <!-- row -->
   </div> <!-- container-fluid -->

   <!-- Login Modal -->
   <div id="signinModal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
     <div class="modal-dialog">
       <div class="modal-content">
         <div class="modal-header">
           <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
           <h1 class="modal-title">Sign in</h1>
         </div>
         <div class="modal-body">
           <form class="form col-md-12 center-block" method="POST">
             <div class="form-group">
               <input type="text" class="form-control input-lg" id="username" name="username" placeholder="Username"/>
             </div>
             <div class="form-group">
               <input type="password" class="form-control input-lg" id="password" name="password" placeholder="Password"/>
             </div>
             <div class="form-group">
               <button class="btn btn-primary btn-lg btn-block">Sign in</button>
             </div>
           </form>
         </div>
         <div class="modal-footer" style="border-top: 0px;"><!-- border-top: 0px で余計な横線を消す -->
             <button type="button" class="btn btn-danger" data-dismiss="modal" aria-hidden="true">Cancel</button>
         </div>
       </div>
     </div>
   </div>

   <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
   <script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
 </body>
</html>

Navigation bar is put on top of screen, and click Sign in button which put on right end of Navigation bar shows modal which can input user name and password. Click Sign in button that put on bottom of modal moving the process to login() method in app.py and operate login.

Edit index.html

Finally, edit index.html by following.

<!DOCTYPE html>
<html lang="ja">
 <head>
   <meta charset="utf-8"></meta>
   <meta http-equiv="X-UA-Compatible" content="IE=edge"></meta>
   <meta content="width=device-width, initial-scale=1" name="viewport"></meta>
   <title>HelloFlask</title>
   <!-- Bootstrap CDN -->
   <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
   <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">

   <link rel="stylesheet" type="text/css" href="../static/css/sample.css">
 </head>
 <body>
   <nav class="navbar navbar-default">
     <div class="container-fluid">
       <div class="navbar-header">
         <a class="navbar-brand">HelloFlask</a>
       </div>
       <ul class="nav navbar-nav navbar-right">
         <li><p class="navbar-text">Signed in as: <a href="#" class="navbar-link" id="username">{{ session.username }}</a></p></li>
         <li><a href="/logout" id="logout">Logout</a></li>
       </ul>
     </div>
   </nav>
   <div class="container-fluid">
     <div class="row">
       <div class="col-sm-12">
         <h1 class="text-center" id="hello">Hello Flask</h1>
         <input type="text" class="form-control" id="text" placeholder="Please input text">
         <button class="btn btn-default" id="button">Change text</button>
       </div>
     </div>
   </div>
   <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
   <script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
   <script type="text/javascript" src="../static/js/sample.js"></script>
 </body>
</html>

Added the part wrapped with nav tag. Put navigation bar which contains logout link. Operate logout() in app.py when click this link.


Now, sample implementation of authentication system is completed. User or session related data should save into session, and implement like session.get('KEY_NAME') or `session['KEY_NAME'] to get data when using it.

This sample app is published at k3nsuk3/HelloFlask.