Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
August 7, 2020 06:40 am GMT

Flask/Python Web Auth made ez!

If you've want to add authentication to your backend, you would use token-based authentication like JWT. JWT stands for jsonwebtoken and is an encrypted token which can contain some payload data like the user id which can be used to perform actions as that user.

The thing with JWT is that you will have to handle expiration, refreshing and blacklisting. Well, there are some libraries like express-jwt link (for node backends) that make it easier, but, authentication is a supported/built in part of a url!

Don't know what I mean? Well, if you've worked with MongoDB Atlas, your database access URL will be something like:

mongodb+srv://username:password@...

So you see, you send in your username and password along with the URL.

Well, but how do I authenticate the user that way?

I hear you ask? Well, that's the point of this tutorial.

Project setup

Let's setup our flask project.

First, let's create a folder for our app:

mkdir flask-auth-test && cd flask-auth-test# Add to .gitignore just in case you want to pushecho "backend/venv" >> .gitignore

Now, we need to set up our flask app.

# Create a virtual environment# pip3 install venv # OR# sudo apt install python3-venv# for debian/ubuntu systems if the below command failspython3 -m venv venv# Activate the venvsource venv/bin/activate# FOR FISH: source venv/bin/activate.fish# Windows: .\venv\Scripts\activatepip install flask flask-sqlalchemy flask-httpauth flask-cors python-dotenvtouch app.py

Setup app

First, let's finish with the boiler-platey code.

from flask import Flask, jsonify, request, gfrom flask_sqlalchemy import SQLAlchemyfrom flask_cors import CORSapp = Flask(__name__)app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///database.db"app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = Falsedb = SQLAlchemy(app)CORS(app)@app.cli.command("migrate")def migrate_cmd():    db.create_all()@app.route('/')def index():    return jsonify("works!")

Now, let's run the app with:

export FLASK_DEBUG=1python3 -m flask run

Then, if we perform a GET request to http://localhost:5000/ then, you will get this output:

$ curl http://localhost:5000"works"

Now, let's do the database.

# ...class User(db.Model):    __tablename__ = "users"    id = db.Column(db.Integer, primary_key=True)    email = db.Column(db.String)    username = db.Column(db.String)    password = db.Column(db.String)    def save(self):        db.session.add(self)        db.session.commit()    def delete(self):        db.session.delete(self)        db.session.commit()

Alright! We can create our database!

python3 -m flask migrate

You should now see a new file called database.db in the folder. Nice!

Adding authentication

Let's now add authentication to our app.

# add this import at the topfrom flask_httpauth import HTTPBasicAuth# ...# now add this under Cors(App)auth = HTTPBasicAuth()# This is used to verify if the password is correct@auth.verify_passworddef verify_password(email: str, password: str):    user = User.query.filter_by(email=email)    if not user:        return False    # Add the user to global variables    g.user = user    return True

Now that the authentication is secure, let's add a register route.

#...@app.route("/register", methods=["POST"])def register():    email = request.json.get("email").lower()    password = request.json.get("password")    if not email or not password:        return jsonify({"success": False, "message": "One or more fields empty or not present"}), 400    if not re.match(pattern=r"[\w_.]{3,}@\w{3,}\.\w{2,}", string=email):        return jsonify({"success": False, "message": "Invalid email"}), 400    user = User.query.filter_by(email=email)    if user:        return jsonify({"success": False, "message": "Account already exists! Choose a different email or username"}), 400    u = User(email=email, password=password)    u.save()    return jsonify({"success": True})

We don't need a login route because flask-httpauth handles that for us. Now, let's add a protected route.

@app.route("/secret")@auth.login_requireddef secret():    return jsonify("never gonna give you up")

The /secret route is now protected and can not be accessed by an unauthorized user. Now, let's try registering:

curl -X POST -H "Content-Type: application/json" -d '{"email: "[email protected]", "password": "a"}' http://localhost:5000/register

And you should get {"success": True}!

Nice!

Now, if you want to access the /secret route, you just add the user like this:

# email:passwordcurl -u [email protected]:a http://localhost:5000

And if everything went well, you should get rickrolled!

Using fetch

But how do I use it in my frontend?
If your frontend uses javascript (which it most likely does), you can fetch the backend like so:

fetch("http://localhost:5000/secret", {    headers: {        # The btoa method encodes the auth string and is available in all browsers.        Authorization: `Basic ${btoa("[email protected]:a")`    }}).then(() => {/*...*/});

Full code

from flask import Flask, jsonify, request, gfrom flask_httpauth import HTTPBasicAuthfrom flask_sqlalchemy import SQLAlchemyfrom flask_cors import CORSapp = Flask(__name__)app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///database.db"app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = Falsedb = SQLAlchemy(app)CORS(app)auth = HTTPBasicAuth()@app.cli.command("migrate")def migrate_cmd():    db.create_all()# This is used to verify if the password is correct@auth.verify_passworddef verify_password(email: str, password: str):    user = User.query.filter_by(email=email)    if not user:        return False    # Add the user to global variables    g.user = user    return True# DBclass User(db.Model):    __tablename__ = "users"    id = db.Column(db.Integer, primary_key=True)    email = db.Column(db.String)    username = db.Column(db.String)    password = db.Column(db.String)    def save(self):        db.session.add(self)        db.session.commit()    def delete(self):        db.session.delete(self)        db.session.commit()# Routes@app.route("/register", methods=["POST"])def register():    email = request.json.get("email").lower()    password = request.json.get("password")    if not email or not password:        return jsonify({"success": False, "message": "One or more fields empty or not present"}), 400    if not re.match(pattern=r"[\w_.]{3,}@\w{3,}\.\w{2,}", string=email):        return jsonify({"success": False, "message": "Invalid email"}), 400    user = User.query.filter_by(email=email)    if user:        return jsonify({"success": False, "message": "Account already exists! Choose a different email or username"}), 400    u = User(email=email, password=password)    u.save()    return jsonify({"success": True})@app.route('/')def index():    return jsonify("works!")@app.route("/secret")@auth.login_requireddef secret():    return jsonify("never gonna give you up")

Conclusion

This method of authentication is much easier than using JWT. Hope you've learned something, and of course, correct my mistakes in the comments! :P


Original Link: https://dev.to/arnu515/flask-python-web-auth-made-ez-26h7

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To