Spaces:
Sleeping
Sleeping
| # routes/auth.py | |
| from flask import Blueprint, request, jsonify | |
| import bcrypt | |
| from bson.objectid import ObjectId | |
| from datetime import datetime, timedelta | |
| import secrets | |
| from db import db | |
| from utils.validators import valid_email, valid_password | |
| from utils.auth import create_token | |
| auth_bp = Blueprint("auth", __name__) | |
| # ----------------------------- | |
| # SIGNUP | |
| # ----------------------------- | |
| def signup(): | |
| data = request.json or {} | |
| name = data.get("name","").strip() | |
| email = data.get("email","").strip().lower() | |
| password = data.get("password","") | |
| if not name or not email or not password: | |
| return jsonify({"error":"Missing fields"}), 400 | |
| if not valid_email(email): | |
| return jsonify({"error":"Invalid email"}), 400 | |
| if not valid_password(password): | |
| return jsonify({"error":"Password too weak (min 8, 1 number, 1 special)"}), 400 | |
| if db.users.find_one({"email": email}): | |
| return jsonify({"error":"User already exists"}), 400 | |
| hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() | |
| user_doc = { | |
| "name": name, | |
| "email": email, | |
| "password": hashed, | |
| "role": "student", | |
| "blocked": False, | |
| "reset_token": None, | |
| "reset_token_expiry": None | |
| } | |
| res = db.users.insert_one(user_doc) | |
| uid = str(res.inserted_id) | |
| return jsonify({"message":"User created", "id": uid}), 201 | |
| # ----------------------------- | |
| # LOGIN | |
| # ----------------------------- | |
| def login(): | |
| data = request.json or {} | |
| email = (data.get("email") or "").strip().lower() | |
| password = data.get("password","") | |
| user = db.users.find_one({"email": email}) | |
| if not user: | |
| return jsonify({"error":"Invalid credentials"}), 401 | |
| if user.get("blocked", False): | |
| return jsonify({"error":"Account blocked"}), 403 | |
| hashed = user["password"] | |
| if not bcrypt.checkpw(password.encode(), hashed.encode()): | |
| return jsonify({"error":"Invalid credentials"}), 401 | |
| token = create_token(user["_id"], user.get("role","student")) | |
| return jsonify({ | |
| "token": token, | |
| "role": user.get("role","student"), | |
| "name": user.get("name") | |
| }) | |
| # ----------------------------- | |
| # FORGOT PASSWORD | |
| # ----------------------------- | |
| def forgot_password(): | |
| data = request.json or {} | |
| email = (data.get("email") or "").strip().lower() | |
| user = db.users.find_one({"email": email}) | |
| if not user: | |
| return jsonify({"error":"User not found"}), 404 | |
| # Generate secure token | |
| token = secrets.token_urlsafe(32) | |
| expiry = datetime.utcnow() + timedelta(hours=1) # valid 1 hour | |
| db.users.update_one( | |
| {"_id": user["_id"]}, | |
| {"$set": {"reset_token": token, "reset_token_expiry": expiry}} | |
| ) | |
| # TODO: send email; for now, print to console | |
| print(f"[RESET LINK] {email}: https://yourfrontend.com/reset-password?token={token}") | |
| return jsonify({"message":"Password reset link sent to email (check console in dev mode)"}), 200 | |
| # ----------------------------- | |
| # RESET PASSWORD | |
| # ----------------------------- | |
| def reset_password(): | |
| data = request.json or {} | |
| token = data.get("token") | |
| new_password = data.get("password") | |
| if not token or not new_password: | |
| return jsonify({"error":"Missing token or password"}), 400 | |
| if not valid_password(new_password): | |
| return jsonify({"error":"Password too weak (min 8, 1 number, 1 special)"}), 400 | |
| user = db.users.find_one({"reset_token": token}) | |
| if not user: | |
| return jsonify({"error":"Invalid or expired token"}), 400 | |
| if not user.get("reset_token_expiry") or user["reset_token_expiry"] < datetime.utcnow(): | |
| return jsonify({"error":"Token expired"}), 400 | |
| hashed_pw = bcrypt.hashpw(new_password.encode(), bcrypt.gensalt()).decode() | |
| db.users.update_one( | |
| {"_id": user["_id"]}, | |
| {"$set": {"password": hashed_pw}, | |
| "$unset": {"reset_token": "", "reset_token_expiry": ""}} | |
| ) | |
| return jsonify({"message":"Password reset successful"}), 200 | |