Cookie Based Authentication with Flask and Vue.js: Part 1

Fareed Idris
4 min readMar 29, 2021

--

A side-by-side image of the flask logo, followed by a picture of a stack of cookies

This is part 1 of how to integrate cookie based authentication into your Flask/Vue application.

This series is from the perspective of running 2 applications, one for your Vue frontend and the other your Flask backend.

Disclaimer: This is not the definitive way to do this nor is it the best way. But it is 1 way of doing it so enjoy.

Assumptions

To simplify the structure of this article, I will make a couple assumptions about the state of your project:

  • You are familiar with Flask and how it works
  • You have a database for storing user credentials such as a username and password

Getting Started

First up is to install the libraries we will be using:

$ pip install flask-jwt-extended

Next, we create a blueprint for accessing authentication endpoints. This can be in a file called auth.py

# endpoints/auth.pyfrom flask import Blueprintbp = Blueprint('auth', __name__, url_prefix="/auth")# __init__.pyfrom authApp.endpoints import auth...
app.register_blueprint(auth.bp)

Authentication Flows

Luckily, the authentication flows for both registering an account and logging in are fairly similar. The only difference would be adding a user to the database if one with the provided details does not exists during the registration flow.

When the registration endpoint is reached, we extract the new user details from the request and check if a user with those credentials already exists. If not, we create a new user model instance and add that to the database. Once the user is created the remainder of the flow is the same as logging in.

We use the Flask-JWT-Extended functions create_access_token and create_refresh_token to create those corresponding tokens. We can then create a response map using the flask jsonify function and use 2 more Flask-JWT-Extended functions, set_access_cookies and set_refresh_cookies, to store the newly created tokens in cookies and add them to the response map.

The response map is then returned to the client, completing the authentication process.

We use the werkzeug.security function generate_password_hash to hash a password because storing plaintext passwords is some pretty weak stuff.

# endpoints/auth.pyfrom flask import request, jsonify
from flask_jwt_extended import create_access_token, create_refresh_token, set_access_cookies, set_refresh_cookies
from authApp.models import User
...@bp.route('/register', methods=('POST',))
def register():
data = request.get_json()
password = data['password']
username = data['username']

user = User.query.filter_by(username=username).first()
if user is None:
user = User(
username=username,
password=generate_password_hash(password)
)

user.add()
access_token = create_access_token(identity=user.user_id)
refresh_token = create_refresh_token(identity=user.user_id)

response = jsonify()
set_access_cookies(response, access_token)
set_refresh_cookies(response, refresh_token)

return response, 201
else:
return jsonify(message="Unable to create user."), 400
@bp.route('/login', methods=('POST',))
def login():
data = request.get_json()
username = data['username']
password = data['password']
user = User.authenticate(username, password)
if user:
access_token = create_access_token(identity=user.user_id)
refresh_token = create_refresh_token(identity=user.user_id)

response = jsonify()
set_access_cookies(response, access_token)
set_refresh_cookies(response, refresh_token)
return response, 201
else:
return jsonify(message="Unauthorized"), 401

Logout is also fairly intuitive. Flask_JWT_Extended provides a unset_jwt_cookies function to unset jwt cookies from a response, so that’s what we do. The response to our frontend no longer contains valid authentication cookies.

from flask_jwt_extended import unset_jwt_cookies, jwt_required@bp.route('/logout', methods=('POST',))
@jwt_required
def logout():
response = jsonify()
unset_jwt_cookies(response)
return response, 200

Securing Endpoints

Once again Flask-JWT-Extended comes to the rescue. We are provided a function, jwt_required, that we use as a decorator for our endpoint functions. With this in place, requests must have a jwt token present to be able to access it, otherwise a default 401 response is returned.

from flask_jwt_extended import jwt_required@bp.route('/secure', methods=('GET',))
@jwt_required
def secured_endpoint():
...

Refreshing Tokens

Refresh tokens are much longer lived tokens that are sent along with access tokens to our flask application. If a request is returned to the frontend as a 401 due to an expired token, a new request is made to a refresh endpoint where we return a new access token and use it to retry the initial request. Flask-JWT-Extended provides a more in depth explanation into how this works here.

from flask_jwt_extended import jwt_refresh_token_required@bp.route('/refresh', methods=('POST',))
@jwt_refresh_token_required
def refresh():
user_id = get_jwt_identity()
user = User.query.filter_by(user_id=user_id).first()
access_token = create_access_token(identity=user.user_id)

response = jsonify()
set_access_cookies(response, access_token)

return response, 201

We use the jwt_refresh_token_required decorator here since the access token is expired and we’re relying on the refresh token for this request. Plus we only want users to access this endpoint with a valid refresh token.

Configuration Options

There are a couple config options used to make everything work:

JWT_SECRET_KEY={secret_key}— Used to encode and decode jwt token. “Keep it secret. Keep it safe.”

JWT_TOKEN_LOCATION=[‘cookies’] — Well, this is a series on cookie based authentication. Flask-JWT-Extended allows storing jwt’s in other parts of a request but that’s outside the scope of this series.

JWT_COOKIE_SECURE=True— True means cookies will only be sent over an HTTPS connection. You usually want this true in production. It defaults to False, making development easier.

There are a lot of useful configuration options made available by Flask-JWT-Extended, I’d highly recommend checking them out here.

Conclusion

So far what we have is an API that accepts user credentials, validates them and creates authentication tokens that are stored in cookies. So far so good. Next is to handle our login and token refresh flows on the frontend.

--

--