diff --git a/api/app.py b/api/app.py index ec6c1c8..e3af730 100644 --- a/api/app.py +++ b/api/app.py @@ -2,10 +2,10 @@ from dotenv import load_dotenv from flask import Flask, jsonify from flask_jwt_extended import JWTManager from jwt import ExpiredSignatureError -from models import db +from models import db, RevokedToken import os -from task_views import task_bp -from user_views import user_bp, init_db +from utils import init_db +from views import user_bp from werkzeug.exceptions import HTTPException def create_app(config_name="default"): @@ -19,18 +19,24 @@ def create_app(config_name="default"): app.config["TESTING"] = True else: app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv("SQLALCHEMY_DATABASE_URI") - app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False + + # JWT settings app.config["JWT_SECRET_KEY"] = os.getenv("JWT_SECRET_KEY", "changeme") # Blueprints registration app.register_blueprint(user_bp) - app.register_blueprint(task_bp) # Database and JWT initialization db.init_app(app) jwt = JWTManager(app) + # Function to check if JWT token is revoked + @jwt.token_in_blocklist_loader + def check_if_token_revoked(jwt_header, jwt_payload): + token = db.session.get(RevokedToken, jwt_payload["jti"]) + return token is not None + # Global error handler @app.errorhandler(Exception) def global_error_handler(error): diff --git a/api/models.py b/api/models.py index ac0c674..ef4e85e 100644 --- a/api/models.py +++ b/api/models.py @@ -15,3 +15,6 @@ class User(db.Model): @staticmethod def get_editable_fields(): return {"username", "email", "role", "password"} + +class RevokedToken(db.Model): + jti = db.Column(db.String(100), primary_key=True) diff --git a/api/views.py b/api/views.py index ac6bb9d..5bb1a53 100644 --- a/api/views.py +++ b/api/views.py @@ -1,6 +1,7 @@ from flask import Blueprint, jsonify, request, abort -from flask_jwt_extended import create_access_token, set_access_cookies, jwt_required, verify_jwt_in_request, get_jwt_identity, unset_jwt_cookies -from models import User, db +from flask_jwt_extended import create_access_token, set_access_cookies, jwt_required, \ +verify_jwt_in_request, get_jwt_identity, unset_jwt_cookies, get_jwt +from models import db, RevokedToken, User from utils import admin_required, validate_access from werkzeug.security import check_password_hash, generate_password_hash @@ -102,6 +103,10 @@ def user_login(): @user_bp.route('/logout', methods=['GET']) @jwt_required() def user_logout(): + jti = get_jwt()["jti"] + revoked_token = RevokedToken(jti=jti) + db.session.add(revoked_token) + db.session.commit() response = jsonify({"msg": "User logged out successfully."}) unset_jwt_cookies(response) return response