# Making a 'login required' decorator

Earlier this section we wrote a simple check in our /protected endpoint which acted as a gate for logged-out users.

If the user is not logged in, they are redirected to the login page. Otherwise, the rest of the route runs as normal.

This is such a common thing to do, that it's likely almost all your endpoints will have a check like that one:

@app.get("/protected")
def protected():
    if not session.get("email"):
        abort(401)
    return render_template("protected.html")

# Writing the decorator

A decorator in Python is a function that acts on another function, extending it by running some code either before or after it.

I'd recommend learning about decorators in depth[1] before continuing!

Here's what our decorator looks like:

def login_required(route):
    @functools.wraps(route)
    def route_wrapper(*args, **kwargs):
        if session.get("email") is None:
            return redirect(url_for("login"))

        return route(*args, **kwargs)

    return route_wrapper

And this is how we use it:

 @app.get("/protected")
+@login_required
 def protected():
     return render_template("protected.html")

With that, the protected function is replaced by the route_wrapper function (although it keeps its name and docstring, if any):

def route_wrapper(*args, **kwargs):
    if session.get("email") is None:
        return redirect(url_for("login"))

    return route(*args, **kwargs)

Note that route(*args, **kwargs) is calling what was previously the protected function. What we've done is extracted the log in check so that any of our endpoints can be decorated with @login_required so the check runs before anything else does in the endpoint.


  1. How to write decorators in Python (The Teclado Blog) (opens new window) ↩ī¸Ž