Easy Authentication With Flask
Easily implement auth routing in your Flask Application

If you want to build Flask applications fast to proof test (POC) an idea, reseach & development or to simply get your app out on the market as soon as possible (thinking MVP …?) , then Flask JWT Router could be your answer.
flask-JWT-Routes provides the following
- Out of the box authentication with JSON Web tokens
- White listed routes with optional api name prefix e.g /api/v1
- Optional authenticated entities (supports multiple entities eg. users, admins etc.) available on Flask’s g object
Authentication vs Authorization
The differences between authentication vs authorization can be confusing, so let’s first etablish the differences.
Authentication
- Verifies you are a valid user.
- Login forms & tokens can be used to authenticate the user
Authorization
- Decides if you have permission to access a resource
So to summarize, authentication verifies who you are & authorization verifies what you are authorized to do.
So let’s get started! First of all install flask-jwt-router using pip:
pip install flask-jwt-routerYou’ll then need to pass your Flask application instance to the JwtRoutes class. If you are using the ‘Flask Factory Pattern” then you can also call the JwtRoutes’s init_app method & pass the Flask app instance to the create_app function (see https://flask.palletsprojects.com/en/1.1.x/patterns/appfactories/ for more details)
from flask import Flask
from flask_jwt_router import JwtRoutes# 1. Simple Flask app
app = Flask(__name__)
JwtRoutes(app)
# 2. If you're using the Flask factory pattern:
jwt_routes = JwtRoutes()
def create_app(config):
...
jwt_routes.init_app(app)
You can then decide which of your app’s routes don’t need authorizition (remember authorization verifies what the users can do). Simply, pass a list of Tuple pairs to your Flask app’s config dict:
app.config["WHITE_LIST_ROUTES"] = [
("POST", "/register"),
]Now, if someone were to navigate their browser to https://example.com/register, and submit a registration form, they wouldn’t be unauthorized:
@app.route("/register", methods=["POST"])
def register():
return "I don't need authorizing!"Prefix your api name to whitelisted routes
A really useful feature is to define once your API Name, this saves times if you have many whitelisted routes:
app.config["JWT_ROUTER_API_NAME"] = "/api/v1"
app.config["WHITE_LIST_ROUTES"] = [
("POST", "/register"),
]
@app.route("/api/v1/register", methods=["POST"])
def register():
return "I don't need authorizing!"We could also whitelist more paths like this:
white_list = [
("GET", "/cats"),
("POST", "/dogs"),
("PUT", "/fruit"),
("DELETE", "/clouds"),Then we pass this list to the apps config:
app.config["WHITE_LIST_ROUTES"] = white_listAnd now all these routes are authorized to all users:
@app.route("/cats", methods=["GET"])
def register():
return "I don't need authorizing!"
@app.route("/dogs", methods=["POST"])
def register():
return "I don't need authorizing!"
@app.route("/fruit", methods=["PUT])
def register():
return "I don't need authorizing!"
@app.route("/clouds", methods=["DELETE"])
def register():
return "I don't need authorizing!"Bypass Flask-JWT-Router on specific routes
We can also bypass Flask-JWT-Router on specific routes using -IGNORED_ROUTES configuration setting. This is useful, for when you want to allow routes to be unaffected by the prefixed API name (when using JWT_ROUTER_API_NAME setting). If you wanted to combine, for example, HTML templates & JSON responses in the same code base then all your endpoints that are meaningful URLs won’t get prefixed with /api/v1 but your endpoints that return resources (JSON) will get the API name prefixed.
# Define homepage template routes for example on JWT_IGNORE_ROUTES
# & still get to use the api name on request handle returning resources
app.config["IGNORED_ROUTES"] = [
("GET", "/")
]Create Authenticated Users With Ease
Another feature of Flask JWT Router is that it can add authentication to any of your entities. As SqlAlchemy is the most popular ORM used with Flask, this is fully supported.
In this example, we will use SQLAlchemy as our ORM library.
First lets import all the dependencies:
from flask import Flask, jsonify, g
from flask_sqlalchemy import SQLAlchemy
from your_app import jwt_routesNotice we import our JwtRoutes instance: your_app import jwt_routes (where your_app is a reference the Flask application instanceapp = Flask(__name__)).
Create an SQLAlchmemy UserModel class passing the db instance:
db = SQLAlchemy(app)
class UserModel(db.Model):
user_id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)Initiate Flask-JWT-Router library:
JwtRoutes(app)# You can also initiate your app within the Flask Factory Pattern:
jwt_routes = JwtRoutes()
jwt_routes.init_app(app)
Then, we need to pass all our SQLAlchemy (or other ORM models) to Flask-JWT-Router like this:
app.config["ENTITY_MODELS"] = [(UserModel), (TeacherModel)]Now, when a user makes a request to one of your resources, Flask-JWT-Router will automatically return the entity’s data, even if you have multiple types o entities requiring authentication.
Entity Primary Keys
Flask JWT Router predicts what the primary key on a SqlAlchemy model for you. Incase you require a specific primary key (you might have for example, multiple primary keys on a single Model) then you can specify which key Flask-JWT-Router should use (although Flask-JWT-Router will work fine, even if you have multiple Primary keys):
app.config["ENTITY_KEY"] = "user_id"Create a new entity & return a new token:
from app import app, jwt_routes@app.route("/register", methods=["POST"])
def register():
user_data = request.get_json()
try:
user = UserModel(**user_data)
user.create_user() # your entity creation logic
# Here we pass the id as a kwarg to `register_entity`
token: str = jwt_routes.register_entity(entity_id=user.id, entity_type="users")
# Now we can return a new token!
return {
"message": "User successfully created.",
"token": str(token), # casting is optional
}, 200We can now access the entity on any authenticated route.
To access the entity we created above (in this case the user object), we use Flask’s built in global context — g (you can read about Flasks global context here: https://flask.palletsprojects.com/en/1.1.x/appcontext/).
from app import app, jwt_routes@app.route("/login" methods=["GET"])
def register():
user_data = g.get("entity")
try:
user_dumped = UserSchema().dump(user_data)
except ValidationError as _:
return {
"error": "User requested does not exist."
}, 401
return {
"data": user_dumped,
"token": jwt_routes.update_entity(entity_id=user_data.id),
}, 200
Above, we first import our instance of JwtRoutes from our app.py or __init_.py) module. Then we have access to our entity, in this case let’s imagine we are getting our User. Then, a common scenario maybe to use a serialising library to dump the database object to a python Dict, if all goes well then we can return the user data plus an updated token. By calling jwt_routes.update_entity and passing the user’s id we then get an updated token with a new 30 day expire. We, slightly jumped ahead here, below i’ll give you a more detailed run through of return tokens from routes.
Create & Update Tokens on Your Routes
To return a JWT token from any route, we need to import out Flask application instance (see above).
from my_app import jwt_routesCreating a Fresh JWT
Let’s say your user is registering or even logging in without a token, you are able to generate a brand new token with JwtRoutes’sregister_entity method. By passing a kwarg of entity_type we are letting FLASK-JWT-Router know what type of entity needs to be available on Flask’s g context, the next time the entity (now with a token) request that particular resource.
*Warning: The entity_type must be the same as your tablename or __tablename__ attribute value. (With SqlAlchemy, you can define a __tablename__ attribute directly or else the name is derived from your entity’s database table name).
We can register a new user & return a token:user = UserModel(name="John Doe")
token = jwt_routes.register_entity(entity_id=user.id, entity_type="users")
print(token) # "eyJ0eXAiO...
Now when user John Doe wants to login, we can also update his token and return it back to him:
user = db.query.filter_by(id=1).one()
token = jwt_routes.update_entity(entity_id=user.id)
print(token) # "eyJ0eXAiO..."The full code above looks like this:
from flask import Flask, jsonify, g
from flask_sqlalchemy import SQLAlchemy
from flask_jwt_router import JwtRoutesapp = Flask(__name__)
JwtRoutes(app)db = SQLAlchemy(app)app.config["WHITE_LIST_ROUTES"] = [
("PUT", "/auth/user/<int:user_id>"),
("GET", "/auth/user"),
("POST", "/auth/user")
]
app.config["IGNORED_ROUTES"] = [
("GET", "/"),
]
app.config["JWT_ROUTER_API_NAME"] = "/api/v1"
app.config["ENTITY_MODELS"] = [(UserModel), (TeacherModel)]class UserModel(db.Model):
user_id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)rh = RouteHelpers(app)
@app.route("/auth/user", methods=["POST"])
def register():
user_data = request.get_json()
try:
user = UserModel(**user_data)
user.create_user() # your entity creation logic
# Here we pass the id as a kwarg to `register_entity`
token: str = jwt_routes.register_entity(entity_id=user.id, entity_type="users")
# Now we can return a new token!
return {
"message": "User successfully created.",
"token": str(token), # casting is optional
}, 200@app.route("/auth/user", methods=["GET"])
def login():
if "users" not in g:
abort(404)
try:
teacher_dumped = UserSchema().dump(g.teachers)
return {
"data": teacher_dumped,
"token": jwt_routes.update_entity(entity_id=g.teachers.teacher_id),
}, 200
except ValidationError as _:
abort(404)
We can also work directly off the global context g if we have methods attached to out entity:
@app.route("/auth/user", methods=["PUT"])
def update_user():
if "users" not in g:
abort(404)
user_entries = request.get_json()
g.teachers.update_teacher(g.users.user_id, user_entries)
return {
"message": f"Teacher successfully updated.",
"token": jwt_routes.update_entity(entity_id=g.users.user_id),
}, 200