Skip to content

Latest commit

 

History

History
149 lines (104 loc) · 5.51 KB

File metadata and controls

149 lines (104 loc) · 5.51 KB

How to Guide – 2FA using Google Authenticator and Flask

How to add an additional layer of security to the Flask app using Google Authenticator. Which requires the addition of a page in-between the login page and the end page the user is aiming to access.

Install the required libraries

pip install qrcode
pip install pyotp

The QRcode package is issued to generate the image (typically a PNG file) and render the QR codes directly to the console. In this case, we will send the file name to our HTML page to be rendered.

The pyotop package is used the generate the one-time password and is used to implement two-factor authentication in web applications. Further information is provided in the article pyotp 2.9.0.

from flask import Flask, render_template, request, redirect, url_for, session
import userManagement as dbHandler
import pyotp
import pyqrcode
import os
import base64
from io import BytesIO

Adding 2FA

Start by adding a secret key variable at the beginning of your Python file. This will be used to securely sign the session cookies and can be used for any other security-related needs.

app = Flask(__name__)
app.secret_key = 'my_secret_key'

We now need to redirect (update) the login button, when successful, to the 2FA page, which will display the QRcode and ask for the code (return redirect(url_for('enable_2fa')) #redirect to 2FA page).

We also need to add in code to generate the one-time passcode (user_secret = pyotp.random_base32()).

def home():
    user_secret = pyotp.random_base32() #generate the one-time passcode
    return redirect(url_for('enable_2fa')) #redirect to 2FA page

Create the HTML page

Create the index HTML page within the template folder and name it index.html.

We then need to insert the code below, which displays the QRCode generated by the Python file, renderings it to the HTML using the IMG tag and requests that the one-time passcode be entered using a form.

This tag in HTML adds the QRcode generated in the Python file. Make sure the name used within the { } brackets, in this case, qr_code, matches the one created in the Python file.

<img src="data:image/png;base64,{{ qr_code }}">
<h1>Welcome Enable 2FA {{ value }}!</h1>
<h1>Scan this QR Code with Google Authenticator</h1>
<img src="data:image/png;base64,{{ qr_code }}">

We then need to add a form to the page to get the code entered by the user after they scan the QRcode.

Again, make sure the name you assign to the input matches the one used in the Python file, in this case, OTP.

<form action="/index.html" method="post">
    <label for="otp">Enter the OTP from your app:</label><br>
    <input type="text" id="otp" name="otp"><br>
    <input type="submit" value="Enable 2FA">
</form>

Create Routes in Flask

Create routes to handle HTTP requests (like GET and POST) when a user submits the one-time passcode.

@app.route('/index.html', methods=['POST', 'GET'])
@app.route('/', methods=['POST', 'GET'])

Create a function to generate the QRcode image and one-time passcode then verify if it matches

In this section of code, we need to generate a secret key for the user.

def home():
    user_secret = pyotp.random_base32()

  We are now going to generate the QRCode and one-time passcode.

The line of code below is used generate the one time passcode based on the secret key generated in the previous step. This will then be used to generate the QRCode using the inbuilt function totp.provisioning_uri(name=username,issuer_name="YourAppName").

totp = pyotp.TOTP(user_secret)

Note: the name of the image created is qr_code.png. Remember this name needs to match the one used in the HTML file.

The line, qr_code.png(stream, scale=5), allows you to adjust the size of the QR Code

The final line of code is used to encode binary data into printable ASCII characters and decoding such encodings back to binary data.

totp = pyotp.TOTP(user_secret)
otp_uri = totp.provisioning_uri(name=username,issuer_name="YourAppName")
qr_code = pyqrcode.create(otp_uri)
stream = BytesIO()
qr_code.png(stream, scale=5)
qr_code_b64 = base64.b64encode(stream.getvalue()).decode('utf-8')

All that is left is to validate the entry of the one-time passcode and redirect the user to the desired page.

In this section we are retrieving the input from the form using the line of code:

otp_input = request.form[‘otp’]

We then use the inbuilt function totp.verify to validate the code entered matches the one generated by Google Authenticator app. If the valid code matches, we direct the user to the desired page, as they have successfully logged on using 2FA.

Note, you will need to either created a new some_page.html page or change the name to the page you wish to display.

You may also wish to create a page which indicates the code entered is invalid as currently it simply displays a plain message to a blank page.

if request.method == 'POST':
    otp_input = request.form['otp']
    if totp.verify(otp_input):
        return render_template('some_page.html')
        #return redirect(url_for('home'))  # Redirect to home if OTP is valid
    else:
        return "Invalid OTP. Please try again.", 401

return render_template(index.html')

Run Your Flask App:

python app.py

Finally, run the Flask app, and test the newly created page using the Google Authenticator app. Note, you may need to rescan the qrcode every time modifications are made to the code.