An Interest In:
Web News this Week
- April 1, 2024
- March 31, 2024
- March 30, 2024
- March 29, 2024
- March 28, 2024
- March 27, 2024
- March 26, 2024
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 usesjavascript
(which it most likely does), you canfetch
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
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To