feat: implement long-term user login with refresh tokens
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m23s
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m23s
- Introduced a dual-token system for user authentication: a short-lived access token and a long-lived rotating refresh token. - Created a new RefreshToken model to manage refresh tokens securely. - Updated auth_api.py to handle login, refresh, and logout processes with the new token system. - Enhanced security measures including token rotation and theft detection. - Updated frontend to handle token refresh on 401 errors and adjusted SSE authentication. - Removed CORS middleware as it's unnecessary behind the nginx proxy. - Added tests to ensure functionality and security of the new token system.
This commit is contained in:
@@ -1,61 +1,12 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
from api.utils import get_validated_user_id
|
||||
from api.utils import get_validated_user_id, admin_required
|
||||
from db.tracking import get_tracking_events_by_child, get_tracking_events_by_user
|
||||
from models.tracking_event import TrackingEvent
|
||||
from functools import wraps
|
||||
import jwt
|
||||
from tinydb import Query
|
||||
from db.db import users_db
|
||||
from models.user import User
|
||||
|
||||
|
||||
tracking_api = Blueprint('tracking_api', __name__)
|
||||
|
||||
|
||||
def admin_required(f):
|
||||
"""
|
||||
Decorator to require admin role for endpoints.
|
||||
"""
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
# Get JWT token from cookie
|
||||
token = request.cookies.get('token')
|
||||
if not token:
|
||||
return jsonify({'error': 'Authentication required', 'code': 'AUTH_REQUIRED'}), 401
|
||||
|
||||
try:
|
||||
# Verify JWT token
|
||||
payload = jwt.decode(token, 'supersecretkey', algorithms=['HS256'])
|
||||
user_id = payload.get('user_id')
|
||||
|
||||
if not user_id:
|
||||
return jsonify({'error': 'Invalid token', 'code': 'INVALID_TOKEN'}), 401
|
||||
|
||||
# Get user from database
|
||||
Query_ = Query()
|
||||
user_dict = users_db.get(Query_.id == user_id)
|
||||
|
||||
if not user_dict:
|
||||
return jsonify({'error': 'User not found', 'code': 'USER_NOT_FOUND'}), 404
|
||||
|
||||
user = User.from_dict(user_dict)
|
||||
|
||||
# Check if user has admin role
|
||||
if user.role != 'admin':
|
||||
return jsonify({'error': 'Admin access required', 'code': 'ADMIN_REQUIRED'}), 403
|
||||
|
||||
# Store user_id in request context
|
||||
request.admin_user_id = user_id
|
||||
return f(*args, **kwargs)
|
||||
|
||||
except jwt.ExpiredSignatureError:
|
||||
return jsonify({'error': 'Token expired', 'code': 'TOKEN_EXPIRED'}), 401
|
||||
except jwt.InvalidTokenError:
|
||||
return jsonify({'error': 'Invalid token', 'code': 'INVALID_TOKEN'}), 401
|
||||
|
||||
return decorated_function
|
||||
|
||||
|
||||
@tracking_api.route('/admin/tracking', methods=['GET'])
|
||||
@admin_required
|
||||
def get_tracking():
|
||||
|
||||
Reference in New Issue
Block a user