feat(auth): require email confirmation for new accounts
Updates the user registration and new account creation endpoints to require email confirmation. - Sets the 'confirmed' flag to 'false' by default for all new user accounts. - Generates a unique confirmation token for each new user. - Logs the confirmation link to the console for development purposes. This change ensures that users cannot log in without first verifying their email address, enhancing account security.
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
import secrets
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
# Existing imports...
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from database import get_db
|
||||
from models import Account_User, Customer_Customer
|
||||
from schemas import UserCreate, UserResponse
|
||||
from schemas import UserCreate
|
||||
from passlib.context import CryptContext
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
# Existing pwd_context and helper functions...
|
||||
pwd_context = CryptContext(schemes=["pbkdf2_sha256"], deprecated="auto")
|
||||
|
||||
def get_password_hash(password):
|
||||
@@ -25,14 +30,13 @@ def escape_like_pattern(value: str) -> str:
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/register", response_model=UserResponse)
|
||||
@router.post("/register")
|
||||
async def register(user: UserCreate, db: AsyncSession = Depends(get_db)):
|
||||
# Verify passwords match
|
||||
if user.password != user.confirm_password:
|
||||
raise HTTPException(status_code=400, detail="Passwords do not match")
|
||||
|
||||
# Check if customer exists in Customer_Customer table
|
||||
# Escape SQL LIKE wildcards to prevent injection attacks
|
||||
escaped_house_number = escape_like_pattern(user.house_number)
|
||||
customer_result = await db.execute(
|
||||
select(Customer_Customer).where(
|
||||
@@ -51,21 +55,31 @@ async def register(user: UserCreate, db: AsyncSession = Depends(get_db)):
|
||||
|
||||
username = f"{user.account_number}-{user.house_number}"
|
||||
hashed_password = get_password_hash(user.password)
|
||||
token = secrets.token_urlsafe(32)
|
||||
|
||||
db_user = Account_User(
|
||||
username=username,
|
||||
account_number=user.account_number,
|
||||
house_number=user.house_number,
|
||||
password_hash=hashed_password,
|
||||
member_since=datetime.utcnow(),
|
||||
member_since=datetime.now(timezone.utc),
|
||||
email=user.email,
|
||||
last_seen=datetime.utcnow(),
|
||||
last_seen=datetime.now(timezone.utc),
|
||||
admin=0,
|
||||
admin_role=0,
|
||||
confirmed=1,
|
||||
confirmed=0,
|
||||
active=1,
|
||||
user_id=customer.id
|
||||
user_id=customer.id,
|
||||
confirmation_token=token,
|
||||
confirmation_sent_at=datetime.now(timezone.utc)
|
||||
)
|
||||
db.add(db_user)
|
||||
await db.commit()
|
||||
await db.refresh(db_user)
|
||||
return db_user
|
||||
|
||||
# In a real application, you would send an email here
|
||||
# For now, we'll just print the confirmation URL to the console
|
||||
confirmation_url = f"http://localhost:3000/confirm-email?token={token}"
|
||||
print(f"Confirmation URL: {confirmation_url}")
|
||||
|
||||
return JSONResponse(status_code=201, content={"message": "Registration successful. Please check your email to confirm your account."})
|
||||
|
||||
Reference in New Issue
Block a user