Fix admin authentication in production environment with persistent sessions
This commit is contained in:
@@ -10,16 +10,60 @@ load_dotenv()
|
|||||||
# Initialize SQLAlchemy
|
# Initialize SQLAlchemy
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
|
|
||||||
|
# Get or generate a stable SECRET_KEY
|
||||||
|
def get_secret_key():
|
||||||
|
# Check for an existing key file
|
||||||
|
key_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../instance/secret_key')
|
||||||
|
|
||||||
|
# If running in production with Docker, store in a more appropriate location
|
||||||
|
if os.environ.get('FLASK_ENV') == 'production':
|
||||||
|
key_file = '/data/secret_key'
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to read existing key
|
||||||
|
if os.path.exists(key_file):
|
||||||
|
with open(key_file, 'rb') as f:
|
||||||
|
key = f.read()
|
||||||
|
if key:
|
||||||
|
return key
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Generate a new key
|
||||||
|
key = secrets.token_hex(32).encode()
|
||||||
|
|
||||||
|
# Try to save it for future use
|
||||||
|
try:
|
||||||
|
os.makedirs(os.path.dirname(key_file), exist_ok=True)
|
||||||
|
with open(key_file, 'wb') as f:
|
||||||
|
f.write(key)
|
||||||
|
except:
|
||||||
|
# If we can't save it, just use it for this session
|
||||||
|
pass
|
||||||
|
|
||||||
|
return key
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
app = Flask(__name__, instance_relative_config=True)
|
app = Flask(__name__, instance_relative_config=True)
|
||||||
|
|
||||||
|
# Determine if we're in production
|
||||||
|
is_production = os.environ.get('FLASK_ENV') == 'production'
|
||||||
|
|
||||||
# Configure the app
|
# Configure the app
|
||||||
app.config.from_mapping(
|
app.config.from_mapping(
|
||||||
SECRET_KEY=os.environ.get('SECRET_KEY', secrets.token_hex(16)),
|
SECRET_KEY=os.environ.get('SECRET_KEY', get_secret_key()),
|
||||||
SQLALCHEMY_DATABASE_URI=f"sqlite:///{os.path.join(app.instance_path, 'game_tracker.sqlite')}",
|
SQLALCHEMY_DATABASE_URI=f"sqlite:///{os.path.join(app.instance_path, 'game_tracker.sqlite')}",
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS=False,
|
SQLALCHEMY_TRACK_MODIFICATIONS=False,
|
||||||
|
# Session configuration for secure cookies
|
||||||
|
SESSION_COOKIE_SECURE=is_production, # Only send cookies over HTTPS in production
|
||||||
|
SESSION_COOKIE_HTTPONLY=True, # Prevent JavaScript access to cookies
|
||||||
|
SESSION_COOKIE_SAMESITE='Lax', # Prevent CSRF attacks
|
||||||
|
PERMANENT_SESSION_LIFETIME=86400, # 24 hours
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# If behind a proxy (like Nginx), trust the proxy headers
|
||||||
|
app.config['PREFERRED_URL_SCHEME'] = 'https' if is_production else 'http'
|
||||||
|
|
||||||
# Ensure the instance folder exists
|
# Ensure the instance folder exists
|
||||||
try:
|
try:
|
||||||
os.makedirs(app.instance_path)
|
os.makedirs(app.instance_path)
|
||||||
@@ -33,6 +77,14 @@ def create_app():
|
|||||||
from .routes import main_bp
|
from .routes import main_bp
|
||||||
app.register_blueprint(main_bp)
|
app.register_blueprint(main_bp)
|
||||||
|
|
||||||
|
# Set up session persistence for production
|
||||||
|
if is_production:
|
||||||
|
from flask_session import Session
|
||||||
|
app.config['SESSION_TYPE'] = 'filesystem'
|
||||||
|
app.config['SESSION_FILE_DIR'] = '/data/flask_session'
|
||||||
|
app.config['SESSION_PERMANENT'] = True
|
||||||
|
Session(app)
|
||||||
|
|
||||||
# Create database tables
|
# Create database tables
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
|||||||
@@ -7,8 +7,12 @@ services:
|
|||||||
- "8000"
|
- "8000"
|
||||||
volumes:
|
volumes:
|
||||||
- ./instance:/app/instance
|
- ./instance:/app/instance
|
||||||
|
- ./data/flask_session:/data/flask_session
|
||||||
|
- ./data/secrets:/data
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
environment:
|
||||||
|
- FLASK_ENV=production
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- app-network
|
- app-network
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
Flask==2.3.3
|
Flask==2.3.3
|
||||||
Flask-SQLAlchemy==3.1.1
|
Flask-SQLAlchemy==3.1.1
|
||||||
Flask-WTF==1.2.1
|
Flask-WTF==1.2.1
|
||||||
|
Flask-Session==0.5.0
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
python-dotenv==1.0.0
|
python-dotenv==1.0.0
|
||||||
|
gunicorn==21.2.0
|
||||||
|
|||||||
Reference in New Issue
Block a user