# app/auth/routes.py from flask import Blueprint, render_template, request, redirect, url_for, flash, session from flask_login import login_user, logout_user, login_required from app.utils.extensions import User, get_client_from_session from app.services.appwrite_client import AppwriteAccountClient auth_bp = Blueprint("auth", __name__, url_prefix="") @auth_bp.route("/register", methods=["GET", "POST"]) def register(): if request.method == "POST": email = request.form.get("email", "").strip() password = request.form.get("password", "") name = (request.form.get("name") or "").strip() or None if not email or not password: flash("Email and password are required.", "error") return redirect(url_for("auth.register")) try: # Create user (no bound session needed) aw = AppwriteAccountClient(use_admin=True) aw.create_user(email=email, password=password, name=name) # Create a session and STORE THE SECRET in Flask session sess_obj = aw.create_email_password_session(email=email, password=password) secret = sess_obj.get("secret") session["appwrite_cookies"] = secret # Bind and fetch profile (either reuse aw with binding or make a new instance) aw = AppwriteAccountClient(cookies=secret) profile = aw.get_account() session["user_profile"] = profile # (Optional) Create a JWT for short-lived API calls jwt = aw.create_jwt() session["appwrite_jwt"] = jwt login_user(User(id=profile["$id"], email=profile["email"], name=profile.get("name"))) flash("Account created and you are now logged in.", "success") return redirect(url_for("main.dashboard")) except Exception as e: flash(str(e), "error") return redirect(url_for("auth.register")) return render_template("auth/register.html") @auth_bp.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": email = request.form.get("email", "").strip() password = request.form.get("password", "") if not email or not password: flash("Email and password are required.", "error") return redirect(url_for("auth.login")) try: # 1) Create session once aw = AppwriteAccountClient(use_admin=True) sess_obj = aw.create_email_password_session(email=email, password=password) secret = sess_obj.get("secret") # 2) Save the secret for SSR calls session["appwrite_cookies"] = secret # 3) Bind and load profile aw = AppwriteAccountClient(cookies=secret) profile = aw.get_account() session["user_profile"] = profile # optional: create a JWT now if your dashboard expects it try: session["appwrite_jwt"] = aw.create_jwt() except Exception: session["appwrite_jwt"] = {} usersname = profile.get("name") login_user(User(id=profile["$id"], email=profile["email"], name=profile.get("name"))) flash(f"Welcome back {usersname}!", "success") return redirect(url_for("main.dashboard")) except Exception as e: flash(str(e), "error") return redirect(url_for("auth.login")) # Get method return render_template("auth/login.html") @auth_bp.route("/logout", methods=["GET", "POST"]) @login_required def logout(): secret = session.get("appwrite_cookies") if secret: try: aw = AppwriteAccountClient(cookies=secret) aw.logout_current() # best effort except Exception: pass session.clear() logout_user() flash("Signed out.", "success") return redirect(url_for("auth.login")) @auth_bp.route("/send", methods=["POST"]) @login_required def send(): """ Sends a verification email to the currently logged-in user. Appwrite will redirect the user back to /verify/callback with userId & secret. """ try: aw = get_client_from_session() callback = url_for("auth.callback", _external=True) # Must be allowed in Appwrite -> Platforms aw.send_verification(callback_url=callback) flash("Verification email sent. Please check your inbox.", "info") except Exception as e: flash(f"Could not send verification email: {e}", "error") # Go back to where the user came from, or your dashboard return redirect(request.referrer or url_for("main.dashboard")) @auth_bp.route("/callback", methods=["GET"]) def callback(): """ Completes verification after user clicks the email link. Requires the user to be logged in (Appwrite Account endpoints need a session). If not logged in, we stash the link params and send them to log in first. """ user_id = request.args.get("userId") secret = request.args.get("secret") if not user_id or not secret: flash("Invalid verification link.", "error") return redirect(url_for("auth.login")) # If we don't currently have an Appwrite session, ask them to log in, then resume. if not session.get("appwrite_cookies"): session["pending_verification"] = {"userId": user_id, "secret": secret} flash("Please log in to complete email verification.", "warning") return redirect(url_for("auth.login")) # We have a session; complete verification try: aw = get_client_from_session() aw.complete_verification(user_id=user_id, secret=secret) # Refresh cached profile so templates reflect emailVerification=True profile = aw.get_account() session["user_profile"] = profile # Cleanup any pending state session.pop("pending_verification", None) flash("Email verified! You're all set.", "success") return redirect(url_for("main.dashboard")) except Exception as e: flash(f"Verification failed: {e}", "error") return redirect(url_for("auth.login"))