NoahsKI / plugins /auth_routes.py
noah33565's picture
Upload 447 files
42e2b1d verified
"""
╔══════════════════════════════════════════════════════════════════════════════╗
β•‘ β•‘
β•‘ FLASK INTEGRATION for Authentication Plugin (FIXED) β•‘
β•‘ β•‘
β•‘ FIXES: β•‘
β•‘ - User wird ERST nach Verifizierung gespeichert β•‘
β•‘ - Nach Login -> Redirect zu index_ultimate.html β•‘
β•‘ - Bessere Fehlerbehandlung β•‘
β•‘ β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
"""
from flask import Flask, request, jsonify, session, redirect, send_file
from functools import wraps
import sys
import logging
# Import auth plugin
try:
from plugins.auth_plugin import AuthPlugin
except ImportError:
sys.path.insert(0, '.')
from plugins.auth_plugin import AuthPlugin
# Initialize plugin
auth_plugin = AuthPlugin()
logger = logging.getLogger(__name__)
# Store pending registrations (email -> verification code)
pending_registrations = {}
# ═══════════════════════════════════════════════════════════════════════════════
# DECORATOR FOR PROTECTED ROUTES
# ═══════════════════════════════════════════════════════════════════════════════
def require_auth(f):
"""Decorator to protect routes - requires valid token"""
@wraps(f)
def decorated_function(*args, **kwargs):
# Get token from header or query parameter
token = request.headers.get('Authorization')
if token and token.startswith('Bearer '):
token = token[7:] # Remove 'Bearer ' prefix
if not token:
token = request.args.get('token') or request.json.get('token') if request.json else None
if not token:
return jsonify({
'success': False,
'error': 'Authentication required',
'message': 'Bitte erst anmelden'
}), 401
# Validate token
valid, email = auth_plugin.validate_token(token)
if not valid:
return jsonify({
'success': False,
'error': 'Invalid or expired token',
'message': 'Session abgelaufen, bitte neu anmelden'
}), 401
# Add email to request context
request.user_email = email
return f(*args, **kwargs)
return decorated_function
# ═══════════════════════════════════════════════════════════════════════════════
# FLASK ROUTES
# ═══════════════════════════════════════════════════════════════════════════════
def register_auth_routes(app: Flask):
"""Register authentication routes with Flask app"""
@app.route('/auth/register', methods=['POST'])
def auth_register():
"""
Register new user - IMPROVED VERSION
User wird NICHT sofort gespeichert, nur Email + Code erstellt
"""
try:
data = request.get_json()
if not data:
return jsonify({
'success': False,
'error': 'No data provided'
}), 400
email = data.get('email', '').strip().lower()
password = data.get('password', '')
username = data.get('username', '').strip()
if not email or not password:
return jsonify({
'success': False,
'error': 'Email und Passwort erforderlich'
}), 400
# Check if already registered AND verified
if email in auth_plugin.users and auth_plugin.users[email].verified:
return jsonify({
'success': False,
'error': 'Email bereits registriert und verifiziert. Bitte melde dich an.'
}), 400
# Validate password
if len(password) < 8:
return jsonify({
'success': False,
'error': 'Passwort muss mindestens 8 Zeichen lang sein'
}), 400
# Store pending registration (don't create user yet!)
pending_registrations[email] = {
'password': password,
'username': username or None
}
# Generate verification code
import time
import secrets
import hashlib
from plugins.auth_plugin import VerificationCode, AuthConfig
code = ''.join([str(secrets.randbelow(10)) for _ in range(6)])
expires_at = time.time() + (AuthConfig.VERIFICATION_EXPIRY_MINUTES * 60)
verification = VerificationCode(
email=email,
code=code,
created_at=time.time(),
expires_at=expires_at
)
auth_plugin.verifications[email] = verification
auth_plugin._save_verifications()
# Try to send email
email_sent = auth_plugin.email_service.send_verification_email(email, code)
if email_sent:
logger.info(f"βœ“ Verification code sent to {email}")
return jsonify({
'success': True,
'message': f'Verifizierungscode wurde an {email} gesendet. Bitte ΓΌberprΓΌfe deine Emails.'
}), 200
else:
# Email failed, but code is saved - user can get it from JSON
logger.warning(f"⚠ Email failed for {email}, but code is saved in JSON")
return jsonify({
'success': True,
'message': f'Registrierung vorbereitet. WICHTIG: Email-Versand fehlgeschlagen! Code findest du in: noahski_data/auth/verifications.json'
}), 200
except Exception as e:
logger.error(f"Register endpoint error: {e}")
return jsonify({
'success': False,
'error': str(e)
}), 500
@app.route('/auth/verify', methods=['POST'])
def auth_verify():
"""
Verify email with code - IMPROVED VERSION
User wird ERST JETZT erstellt!
"""
try:
data = request.get_json()
if not data:
return jsonify({
'success': False,
'error': 'No data provided'
}), 400
email = data.get('email', '').strip().lower()
code = data.get('code', '').strip()
if not email or not code:
return jsonify({
'success': False,
'error': 'Email und Code erforderlich'
}), 400
# Check if verification exists
if email not in auth_plugin.verifications:
return jsonify({
'success': False,
'error': 'Keine Verifizierung gefunden. Bitte registriere dich erneut.'
}), 400
verification = auth_plugin.verifications[email]
# Check if expired
if not verification.is_valid():
return jsonify({
'success': False,
'error': 'Verifizierungscode abgelaufen oder zu viele Versuche'
}), 400
# Increment attempts
verification.attempts += 1
auth_plugin._save_verifications()
# Check code
if verification.code != code:
return jsonify({
'success': False,
'error': f'Falscher Code (Versuch {verification.attempts}/3)'
}), 400
# Code is correct! NOW create the user
if email not in pending_registrations:
return jsonify({
'success': False,
'error': 'Registrierung nicht gefunden. Bitte registriere dich erneut.'
}), 400
# Get pending registration data
reg_data = pending_registrations[email]
# Create user NOW (verified from the start)
import time
from plugins.auth_plugin import User
user = User(
email=email,
password_hash=auth_plugin._hash_password(reg_data['password']),
created_at=time.time(),
verified=True, # Already verified!
username=reg_data['username']
)
auth_plugin.users[email] = user
auth_plugin._save_users()
# Clean up
del auth_plugin.verifications[email]
auth_plugin._save_verifications()
del pending_registrations[email]
# Send welcome email
auth_plugin.email_service.send_welcome_email(email, user.username)
logger.info(f"βœ“ User verified and created: {email}")
return jsonify({
'success': True,
'message': 'Email erfolgreich verifiziert! Du kannst dich jetzt anmelden.'
}), 200
except Exception as e:
logger.error(f"Verify endpoint error: {e}")
return jsonify({
'success': False,
'error': str(e)
}), 500
@app.route('/auth/resend', methods=['POST'])
def auth_resend():
"""Resend verification code"""
try:
data = request.get_json()
if not data:
return jsonify({
'success': False,
'error': 'No data provided'
}), 400
email = data.get('email', '').strip().lower()
if not email:
return jsonify({
'success': False,
'error': 'Email erforderlich'
}), 400
# Check if pending registration exists
if email not in pending_registrations:
return jsonify({
'success': False,
'error': 'Keine ausstehende Registrierung gefunden'
}), 400
# Generate new code
import time
import secrets
from plugins.auth_plugin import VerificationCode, AuthConfig
code = ''.join([str(secrets.randbelow(10)) for _ in range(6)])
expires_at = time.time() + (AuthConfig.VERIFICATION_EXPIRY_MINUTES * 60)
verification = VerificationCode(
email=email,
code=code,
created_at=time.time(),
expires_at=expires_at
)
auth_plugin.verifications[email] = verification
auth_plugin._save_verifications()
# Send email
email_sent = auth_plugin.email_service.send_verification_email(email, code)
if email_sent:
return jsonify({
'success': True,
'message': 'Neuer Verifizierungscode wurde gesendet'
}), 200
else:
return jsonify({
'success': True,
'message': 'Code generiert, aber Email-Versand fehlgeschlagen. Code in noahski_data/auth/verifications.json'
}), 200
except Exception as e:
logger.error(f"Resend error: {e}")
return jsonify({
'success': False,
'error': str(e)
}), 500
@app.route('/auth/login', methods=['POST'])
def auth_login():
"""Login user - IMPROVED VERSION with redirect info"""
try:
data = request.get_json()
if not data:
return jsonify({
'success': False,
'error': 'No data provided'
}), 400
email = data.get('email', '').strip().lower()
password = data.get('password', '')
if not email or not password:
return jsonify({
'success': False,
'error': 'Email und Passwort erforderlich'
}), 400
# Get client info
ip_address = request.remote_addr
user_agent = request.headers.get('User-Agent')
success, message, token = auth_plugin.login(email, password, ip_address, user_agent)
response_data = {
'success': success,
'message': message
}
if success and token:
response_data['token'] = token
response_data['user'] = auth_plugin.get_user_info(email)
response_data['redirect'] = '/' # Redirect to main page after login
return jsonify(response_data), 200 if success else 401
except Exception as e:
logger.error(f"Login endpoint error: {e}")
return jsonify({
'success': False,
'error': str(e)
}), 500
@app.route('/auth/logout', methods=['POST'])
@require_auth
def auth_logout():
"""Logout user"""
try:
token = request.headers.get('Authorization', '')[7:] # Remove 'Bearer '
success, message = auth_plugin.logout(token)
return jsonify({
'success': success,
'message': message
}), 200
except Exception as e:
logger.error(f"Logout endpoint error: {e}")
return jsonify({
'success': False,
'error': str(e)
}), 500
@app.route('/auth/validate', methods=['GET', 'POST'])
def auth_validate():
"""Validate token"""
try:
token = request.headers.get('Authorization')
if token and token.startswith('Bearer '):
token = token[7:]
if not token:
token = request.args.get('token') or (request.json.get('token') if request.json else None)
if not token:
return jsonify({
'success': False,
'valid': False,
'error': 'No token provided'
}), 400
valid, email = auth_plugin.validate_token(token)
response_data = {
'success': True,
'valid': valid
}
if valid:
response_data['user'] = auth_plugin.get_user_info(email)
return jsonify(response_data), 200
except Exception as e:
logger.error(f"Validate endpoint error: {e}")
return jsonify({
'success': False,
'error': str(e)
}), 500
@app.route('/auth/user', methods=['GET'])
@require_auth
def auth_user_info():
"""Get current user info"""
try:
email = request.user_email
user_info = auth_plugin.get_user_info(email)
if user_info:
return jsonify({
'success': True,
'user': user_info
}), 200
else:
return jsonify({
'success': False,
'error': 'User not found'
}), 404
except Exception as e:
logger.error(f"User info endpoint error: {e}")
return jsonify({
'success': False,
'error': str(e)
}), 500
@app.route('/auth/stats', methods=['GET'])
def auth_stats():
"""Get authentication statistics"""
try:
stats = auth_plugin.get_stats()
stats['pending_registrations'] = len(pending_registrations)
return jsonify({
'success': True,
'stats': stats
}), 200
except Exception as e:
logger.error(f"Stats endpoint error: {e}")
return jsonify({
'success': False,
'error': str(e)
}), 500
@app.route('/auth/settings', methods=['GET'])
@require_auth
def auth_settings_get():
"""Get user settings - FIXED VERSION"""
try:
email = request.user_email
# Get user data from auth plugin
user_data = auth_plugin.get_user(email)
if not user_data:
return jsonify({
'success': False,
'error': 'User not found'
}), 404
# Return user settings (exclude password)
settings = {
'email': user_data.get('email', email),
'username': user_data.get('username', ''),
'theme': user_data.get('theme', 'dark'),
'language': user_data.get('language', 'de'),
'notifications': user_data.get('notifications', True),
'email_verified': user_data.get('email_verified', False)
}
return jsonify({
'success': True,
'settings': settings
})
except Exception as e:
logger.error(f"Settings GET error: {e}", exc_info=True)
return jsonify({
'success': False,
'error': str(e)
}), 500
@app.route('/auth/settings', methods=['POST', 'PUT'])
@require_auth
def auth_settings_update():
"""Update user settings - FIXED VERSION"""
try:
email = request.user_email
data = request.get_json()
if not data:
return jsonify({
'success': False,
'error': 'No data provided'
}), 400
# Get current user data
user_data = auth_plugin.get_user(email)
if not user_data:
return jsonify({
'success': False,
'error': 'User not found'
}), 404
# Update allowed fields
allowed_fields = ['username', 'theme', 'language', 'notifications']
updated_fields = []
for field in allowed_fields:
if field in data:
user_data[field] = data[field]
updated_fields.append(field)
# Save updated user data
auth_plugin.users[email] = user_data
auth_plugin._save_users()
return jsonify({
'success': True,
'message': 'Settings updated successfully',
'updated_fields': updated_fields
})
except Exception as e:
logger.error(f"Settings UPDATE error: {e}", exc_info=True)
return jsonify({
'success': False,
'error': str(e)
}), 500
@app.route('/auth/change-password', methods=['POST'])
@require_auth
def auth_change_password():
"""Change user password - FIXED VERSION"""
try:
import hashlib
email = request.user_email
data = request.get_json()
if not data:
return jsonify({
'success': False,
'error': 'No data provided'
}), 400
current_password = data.get('current_password', '')
new_password = data.get('new_password', '')
if not current_password or not new_password:
return jsonify({
'success': False,
'error': 'Current and new password required'
}), 400
# Verify current password
user_data = auth_plugin.get_user(email)
if not user_data:
return jsonify({
'success': False,
'error': 'User not found'
}), 404
# Check current password
current_hash = hashlib.sha256(current_password.encode()).hexdigest()
if user_data.get('password') != current_hash:
return jsonify({
'success': False,
'error': 'Current password is incorrect'
}), 400
# Update password
new_hash = hashlib.sha256(new_password.encode()).hexdigest()
user_data['password'] = new_hash
# Save
auth_plugin.users[email] = user_data
auth_plugin._save_users()
return jsonify({
'success': True,
'message': 'Password changed successfully'
})
except Exception as e:
logger.error(f"Password change error: {e}", exc_info=True)
return jsonify({
'success': False,
'error': str(e)
}), 500
@app.route('/auth/user-stats', methods=['GET'])
@require_auth
def auth_user_stats():
"""Get user-specific statistics - FIXED VERSION"""
try:
from datetime import datetime
import time
email = request.user_email
# Get user data
user_data = auth_plugin.get_user(email)
if not user_data:
return jsonify({
'success': False,
'error': 'User not found'
}), 404
# Calculate stats
join_date = datetime.fromtimestamp(user_data.get('created_at', time.time()))
days_since_join = (datetime.now() - join_date).days
stats = {
'username': user_data.get('username', ''),
'email': email,
'member_since': join_date.strftime('%Y-%m-%d'),
'days_active': days_since_join,
'total_messages': user_data.get('total_messages', 0),
'total_images': user_data.get('total_images', 0),
'email_verified': user_data.get('email_verified', False),
'theme': user_data.get('theme', 'dark'),
'language': user_data.get('language', 'de')
}
return jsonify({
'success': True,
'stats': stats
})
except Exception as e:
logger.error(f"User stats error: {e}", exc_info=True)
return jsonify({
'success': False,
'error': str(e)
}), 500
@app.route('/auth/resend-code', methods=['POST'])
def auth_resend_code():
"""Resend verification code - NEW"""
try:
import time
import random
data = request.get_json()
if not data:
return jsonify({
'success': False,
'error': 'No data provided'
}), 400
email = data.get('email', '').strip().lower()
if not email:
return jsonify({
'success': False,
'error': 'Email required'
}), 400
# Check if pending verification exists
if email not in pending_registrations:
return jsonify({
'success': False,
'error': 'No pending verification for this email'
}), 400
# Generate new code
new_code = str(random.randint(100000, 999999))
# Update pending data
pending_registrations[email]['code'] = new_code
pending_registrations[email]['timestamp'] = time.time()
logger.info(f"πŸ”„ Resent verification code for {email}: {new_code}")
return jsonify({
'success': True,
'message': 'Verification code resent',
'code': new_code # In production, send via email
})
except Exception as e:
logger.error(f"Resend code error: {e}", exc_info=True)
return jsonify({
'success': False,
'error': str(e)
}), 500
logger.info("βœ… Authentication routes registered (IMPROVED VERSION WITH ALL FIXES)")
# ═══════════════════════════════════════════════════════════════════════════════
# EXPORT
# ═══════════════════════════════════════════════════════════════════════════════
__all__ = ['register_auth_routes', 'require_auth', 'auth_plugin']