""" Auth Views Blueprint This module provides web UI routes for authentication: - Login page - Registration page - Password reset pages - Email verification All forms use HTMX to submit to the API endpoints. """ from flask import Blueprint, render_template, redirect, url_for, request, session from app.utils.auth import get_current_user, clear_user_session from app.utils.logging import get_logger from app.utils.api_client import get_api_client, APIError # Initialize logger logger = get_logger(__file__) # Create blueprint auth_bp = Blueprint('auth_views', __name__) @auth_bp.route('/') def index(): """ Landing page / home page. If user is authenticated, redirect to character list. Otherwise, redirect to login page. """ user = get_current_user() if user: logger.info("Authenticated user accessing home, redirecting to characters", user_id=user.get('id')) return redirect(url_for('character_views.list_characters')) logger.info("Unauthenticated user accessing home, redirecting to login") return redirect(url_for('auth_views.login')) @auth_bp.route('/login', methods=['GET', 'POST']) def login(): """ Display login page and handle login. GET: If user is already authenticated, redirect to character list. POST: Authenticate via API and set session. """ user = get_current_user() if user: logger.info("User already authenticated, redirecting to characters", user_id=user.get('id')) return redirect(url_for('character_views.list_characters')) if request.method == 'POST': # Get form data email = request.form.get('email', '').strip() password = request.form.get('password', '') if not email or not password: return render_template('auth/login.html', error="Email and password are required") # Call API to authenticate try: api_client = get_api_client() response = api_client.post("/api/v1/auth/login", data={ 'email': email, 'password': password }) # Store user in session if response.get('result') and response['result'].get('user'): session['user'] = response['result']['user'] logger.info("User logged in successfully", user_id=response['result']['user'].get('id')) # Redirect to next page or character list next_url = session.pop('next', None) if next_url: return redirect(next_url) return redirect(url_for('character_views.list_characters')) except APIError as e: logger.warning("Login failed", error=str(e)) return render_template('auth/login.html', error=e.message) logger.info("Rendering login page") return render_template('auth/login.html') @auth_bp.route('/register') def register(): """ Display registration page. If user is already authenticated, redirect to character list. """ user = get_current_user() if user: logger.info("User already authenticated, redirecting to characters", user_id=user.get('id')) return redirect(url_for('character_views.list_characters')) logger.info("Rendering registration page") return render_template('auth/register.html') @auth_bp.route('/forgot-password') def forgot_password(): """ Display forgot password page. Allows users to request a password reset email. """ logger.info("Rendering forgot password page") return render_template('auth/forgot_password.html') @auth_bp.route('/reset-password') def reset_password(): """ Display password reset page. This page is accessed via a link in the password reset email. The reset token should be in the query parameters. """ # Get reset token from query parameters token = request.args.get('token') user_id = request.args.get('userId') secret = request.args.get('secret') if not all([token, user_id, secret]): logger.warning("Reset password accessed without required parameters") # Could redirect to forgot-password with an error message return redirect(url_for('auth_views.forgot_password')) logger.info("Rendering password reset page", user_id=user_id) return render_template( 'auth/reset_password.html', token=token, user_id=user_id, secret=secret ) @auth_bp.route('/verify-email') def verify_email(): """ Display email verification page. This page is accessed via a link in the verification email. The verification token should be in the query parameters. """ # Get verification token from query parameters token = request.args.get('token') user_id = request.args.get('userId') secret = request.args.get('secret') if not all([token, user_id, secret]): logger.warning("Email verification accessed without required parameters") return redirect(url_for('auth_views.login')) logger.info("Rendering email verification page", user_id=user_id) return render_template( 'auth/verify_email.html', token=token, user_id=user_id, secret=secret ) @auth_bp.route('/logout', methods=['POST']) def logout(): """ Handle logout by calling API and clearing session. This is a convenience route for non-HTMX logout forms. """ logger.info("Logout initiated via web form") # Call API to logout (this will invalidate session cookie) try: api_client = get_api_client() api_client.post("/api/v1/auth/logout") except APIError as e: logger.error("Failed to call logout API", error=str(e)) # Clear local session clear_user_session() return redirect(url_for('auth_views.login'))