Uso de JWT para la autenticación de usuarios en Flask

Requisito previo: conocimiento básico sobre JSON Web Token (JWT) Asumiré
que tiene el conocimiento básico de JWT y cómo funciona JWT. Si no es así, sugiero leer el artículo de Geeksforgeeks vinculado.

Saltemos directamente a la configuración. Por supuesto, necesita python3 instalado en su sistema. Ahora sígueme. Usaré un entorno virtual donde instalaré las bibliotecas, que sin duda es la mejor manera de hacer cualquier tipo de desarrollo.

  • Primero cree una carpeta llamada Flask Project y cambie el directorio a ella. Si está en Linux, escriba lo siguiente en su terminal.
mkdir "flask project" && cd "flask project"
  • Ahora, cree un entorno virtual. Si está en Linux, escriba lo siguiente en su terminal.
python3 -m venv env

Nota: si recibe algún error, significa que venv no está instalado en su sistema. Para instalarlo, escribe sudo apt install python3-venv en tu terminal y listo. Si está en Windows, use algo como virtualenv para crear un entorno virtual.

Esto creará una carpeta llamada venv en el proyecto del matraz que contendrá las bibliotecas específicas del proyecto. 

  • Ahora cree un archivo llamado requisitos.txt y agregue las siguientes líneas en él.
Flask-RESTful==0.3.8
PyJWT==1.7.1
Flask-SQLAlchemy==2.4.1
  • Ahora, instalemos estas bibliotecas para este proyecto. Para hacerlo, primero debemos activar el entorno virtual. Para hacerlo, escribe lo siguiente en tu terminal.
source env/bin/activate

Nota: si está en Windows, entonces sería Scripts en lugar de bin
Ahora, es hora de instalar las bibliotecas. Para hacerlo, vuelve a escribir lo siguiente en tu terminal. 

pip install -r requirements.txt

Ahora, hemos terminado con la parte de configuración. Ahora comencemos a escribir el código real. Antes de empezar con el código, me gustaría dejar algo claro. Estaría escribiendo todo el código en un solo archivo, es decir, los modelos de base de datos y las rutas en conjunto, lo que no es una buena práctica y definitivamente no es manejable para proyectos más grandes. Intente seguir creando archivos o módulos de python separados para rutas y modelos de bases de datos.
Con eso aclarado, pasemos directamente a escribir el código real. Agregaré comentarios en línea que expliquen cada parte del código.

Cree un archivo python llamado app.py y escriba el siguiente código en él. 

Python3

# flask imports
from flask import Flask, request, jsonify, make_response
from flask_sqlalchemy import SQLAlchemy
import uuid # for public id
from  werkzeug.security import generate_password_hash, check_password_hash
# imports for PyJWT authentication
import jwt
from datetime import datetime, timedelta
from functools import wraps
  
# creates Flask object
app = Flask(__name__)
# configuration
# NEVER HARDCODE YOUR CONFIGURATION IN YOUR CODE
# INSTEAD CREATE A .env FILE AND STORE IN IT
app.config['SECRET_KEY'] = 'your secret key'
# database name
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///Database.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# creates SQLALCHEMY object
db = SQLAlchemy(app)
  
# Database ORMs
class User(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    public_id = db.Column(db.String(50), unique = True)
    name = db.Column(db.String(100))
    email = db.Column(db.String(70), unique = True)
    password = db.Column(db.String(80))
  
# decorator for verifying the JWT
def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = None
        # jwt is passed in the request header
        if 'x-access-token' in request.headers:
            token = request.headers['x-access-token']
        # return 401 if token is not passed
        if not token:
            return jsonify({'message' : 'Token is missing !!'}), 401
  
        try:
            # decoding the payload to fetch the stored details
            data = jwt.decode(token, app.config['SECRET_KEY'])
            current_user = User.query\
                .filter_by(public_id = data['public_id'])\
                .first()
        except:
            return jsonify({
                'message' : 'Token is invalid !!'
            }), 401
        # returns the current logged in users contex to the routes
        return  f(current_user, *args, **kwargs)
  
    return decorated
  
# User Database Route
# this route sends back list of users
@app.route('/user', methods =['GET'])
@token_required
def get_all_users(current_user):
    # querying the database
    # for all the entries in it
    users = User.query.all()
    # converting the query objects
    # to list of jsons
    output = []
    for user in users:
        # appending the user data json
        # to the response list
        output.append({
            'public_id': user.public_id,
            'name' : user.name,
            'email' : user.email
        })
  
    return jsonify({'users': output})
  
# route for logging user in
@app.route('/login', methods =['POST'])
def login():
    # creates dictionary of form data
    auth = request.form
  
    if not auth or not auth.get('email') or not auth.get('password'):
        # returns 401 if any email or / and password is missing
        return make_response(
            'Could not verify',
            401,
            {'WWW-Authenticate' : 'Basic realm ="Login required !!"'}
        )
  
    user = User.query\
        .filter_by(email = auth.get('email'))\
        .first()
  
    if not user:
        # returns 401 if user does not exist
        return make_response(
            'Could not verify',
            401,
            {'WWW-Authenticate' : 'Basic realm ="User does not exist !!"'}
        )
  
    if check_password_hash(user.password, auth.get('password')):
        # generates the JWT Token
        token = jwt.encode({
            'public_id': user.public_id,
            'exp' : datetime.utcnow() + timedelta(minutes = 30)
        }, app.config['SECRET_KEY'])
  
        return make_response(jsonify({'token' : token.decode('UTF-8')}), 201)
    # returns 403 if password is wrong
    return make_response(
        'Could not verify',
        403,
        {'WWW-Authenticate' : 'Basic realm ="Wrong Password !!"'}
    )
  
# signup route
@app.route('/signup', methods =['POST'])
def signup():
    # creates a dictionary of the form data
    data = request.form
  
    # gets name, email and password
    name, email = data.get('name'), data.get('email')
    password = data.get('password')
  
    # checking for existing user
    user = User.query\
        .filter_by(email = email)\
        .first()
    if not user:
        # database ORM object
        user = User(
            public_id = str(uuid.uuid4()),
            name = name,
            email = email,
            password = generate_password_hash(password)
        )
        # insert user
        db.session.add(user)
        db.session.commit()
  
        return make_response('Successfully registered.', 201)
    else:
        # returns 202 if user already exists
        return make_response('User already exists. Please Log in.', 202)
  
if __name__ == "__main__":
    # setting debug to True enables hot reload
    # and also provides a debugger shell
    # if you hit an error while running the server
    app.run(debug = True)

Ahora, nuestro código está listo. Ahora necesitamos crear primero la base de datos y luego la tabla User desde el ORM (Object Relational Mapping). Para hacerlo, primero inicie el intérprete python3 en su terminal. Puede hacerlo escribiendo python3 en su terminal y eso debería funcionar por usted.

A continuación, debe escribir lo siguiente en su intérprete de python3:

from app import db
db.create_all()

Entonces, lo que hace es primero importar el objeto de la base de datos y luego llamar a la función create_all() para crear todas las tablas desde el ORM. Debería verse algo como esto.

python interpreter

Ahora que nuestro código real está listo, vamos a probarlo. Recomiendo usar cartero para probar las API. Puedes usar algo como CURL pero usaré cartero para este tutorial.

Para comenzar a probar nuestra API, primero debemos ejecutar nuestra API. Para hacerlo, abra una ventana de terminal y escriba lo siguiente en ella. 

python app.py

Debería ver una salida como esta 

Python app.py

Si obtiene algún error, asegúrese de que toda la sintaxis y la sangría sean correctas. Puede ver que nuestra API se ejecuta en http://localhost:5000/ . Copia esta URL. Usaremos esta URL junto con las rutas para probar la API.
Ahora, abre Postman . Debería recibir la siguiente pantalla.

Postman Greeting

Ahora, haga clic en el signo + e ingrese la url localhost:5000/signup change request type a POST , luego seleccione Body y luego form-data e ingrese los datos como par clave-valor y luego haga clic en Enviar y debería obtener una respuesta . Debería verse algo como esto. 

Signup

Entonces, estamos registrados. Ahora vamos a iniciar sesión. Para hacer eso, simplemente cambie el punto final a /login y desmarque el campo Nombre y haga clic en Enviar . Debería obtener un JWT como respuesta. Anote ese JWT. Ese será nuestro token y necesitaremos enviar ese token junto con todas las requests posteriores. Este token nos identificará como logueados.

Login

El JSON contiene el token. Anótalo. A continuación, intente obtener la lista de usuarios. Para hacer eso, cambie el punto final a /usuario y luego en la sección de encabezados, agregue un campo como x-access-token y agregue el token JWT en el valor y haga clic en Enviar . Obtendrá la lista de usuarios como JSON.

User

Entonces, así es como puede realizar la autenticación con JWT en Flask. Te recomiendo que practiques más con los JWT y la autenticación de usuarios para que tus conceptos sean más claros.
 

Publicación traducida automáticamente

Artículo escrito por debdutgoswami y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *