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:
@@ -3,7 +3,6 @@ import sys
|
||||
import os
|
||||
|
||||
from flask import Flask, request, jsonify
|
||||
from flask_cors import CORS
|
||||
|
||||
from api.admin_api import admin_api
|
||||
from api.auth_api import auth_api
|
||||
@@ -23,6 +22,7 @@ from config.version import get_full_version
|
||||
from db.default import initializeImages, createDefaultTasks, createDefaultRewards
|
||||
from events.broadcaster import Broadcaster
|
||||
from events.sse import sse_response_for_user, send_to_user
|
||||
from api.utils import get_current_user_id
|
||||
from utils.account_deletion_scheduler import start_deletion_scheduler
|
||||
|
||||
# Configure logging once at application startup
|
||||
@@ -60,10 +60,23 @@ app.config.update(
|
||||
MAIL_PASSWORD='ruyj hxjf nmrz buar',
|
||||
MAIL_DEFAULT_SENDER='ryan.kegel@gmail.com',
|
||||
FRONTEND_URL=os.environ.get('FRONTEND_URL', 'https://localhost:5173'), # Dynamic via env var, defaults to localhost
|
||||
SECRET_KEY='supersecretkey' # Replace with a secure key in production
|
||||
)
|
||||
|
||||
CORS(app)
|
||||
# Security: require SECRET_KEY and REFRESH_TOKEN_EXPIRY_DAYS from environment
|
||||
_secret_key = os.environ.get('SECRET_KEY')
|
||||
if not _secret_key:
|
||||
raise RuntimeError(
|
||||
'SECRET_KEY environment variable is required. '
|
||||
'Set it to a random string (e.g. python -c "import secrets; print(secrets.token_urlsafe(64))")')
|
||||
app.config['SECRET_KEY'] = _secret_key
|
||||
|
||||
_refresh_expiry = os.environ.get('REFRESH_TOKEN_EXPIRY_DAYS')
|
||||
if not _refresh_expiry:
|
||||
raise RuntimeError('REFRESH_TOKEN_EXPIRY_DAYS environment variable is required (e.g. 90).')
|
||||
try:
|
||||
app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = int(_refresh_expiry)
|
||||
except ValueError:
|
||||
raise RuntimeError('REFRESH_TOKEN_EXPIRY_DAYS must be an integer.')
|
||||
|
||||
@app.route("/version")
|
||||
def api_version():
|
||||
@@ -71,11 +84,9 @@ def api_version():
|
||||
|
||||
@app.route("/events")
|
||||
def events():
|
||||
# Authenticate user or read a token
|
||||
user_id = request.args.get("user_id")
|
||||
user_id = get_current_user_id()
|
||||
if not user_id:
|
||||
return {"error": "Missing user_id"}, 400
|
||||
|
||||
return {"error": "Authentication required"}, 401
|
||||
|
||||
return sse_response_for_user(user_id)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user