Files
orion/app/modules/tenancy/routes/api/email_verification.py
Samir Boulahtit d9fc52d47a
Some checks failed
CI / ruff (push) Successful in 10s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has been cancelled
feat: email verification, merchant/store password reset, seed gap fix
- Add EmailVerificationToken and UserPasswordResetToken models with migration
- Add email verification flow: verify-email page route, resend-verification API
- Block login for unverified users (EmailNotVerifiedException in auth_service)
- Add forgot-password/reset-password endpoints for merchant and store auth
- Add "Forgot Password?" links to merchant and store login pages
- Send welcome email with verification link on merchant creation
- Seed email_verification and merchant_password_reset email templates
- Fix db-reset Makefile to run all init-prod seed scripts
- Add UserAuthService to satisfy architecture validation rules
- Add 52 new tests (unit + integration) with full coverage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 23:22:46 +01:00

82 lines
2.6 KiB
Python

# app/modules/tenancy/routes/api/email_verification.py
"""
Email verification API endpoints.
Public endpoints (no auth required):
- POST /resend-verification - Resend verification email
"""
import logging
from fastapi import APIRouter, Depends, Request
from pydantic import BaseModel
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.environment import should_use_secure_cookies
from app.modules.tenancy.models.email_verification_token import (
EmailVerificationToken, # noqa: API-007
)
from app.modules.tenancy.services.user_auth_service import user_auth_service
email_verification_api_router = APIRouter()
logger = logging.getLogger(__name__)
class ResendVerificationRequest(BaseModel):
email: str
class ResendVerificationResponse(BaseModel):
message: str
@email_verification_api_router.post(
"/resend-verification", response_model=ResendVerificationResponse
)
def resend_verification(
request: Request,
body: ResendVerificationRequest,
db: Session = Depends(get_db),
):
"""
Resend email verification link.
Always returns success to prevent email enumeration.
"""
user, plaintext_token = user_auth_service.request_verification_resend(db, body.email)
if user and plaintext_token:
try:
scheme = "https" if should_use_secure_cookies() else "http"
host = request.headers.get("host", "localhost:8000")
verification_link = f"{scheme}://{host}/verify-email?token={plaintext_token}"
from app.modules.messaging.services.email_service import EmailService
email_service = EmailService(db)
email_service.send_template(
template_code="email_verification",
to_email=user.email,
to_name=user.username,
language="en",
variables={
"first_name": user.username,
"verification_link": verification_link,
"expiry_hours": str(EmailVerificationToken.TOKEN_EXPIRY_HOURS),
"platform_name": "Orion",
},
)
db.commit()
logger.info(f"Verification email resent to {user.email}")
except Exception as e:
db.rollback()
logger.error(f"Failed to resend verification email: {e}") # noqa: SEC021
else:
logger.info(f"Resend verification requested for {body.email} (not found or already verified)")
return ResendVerificationResponse(
message="If an account exists with this email and is not yet verified, a verification link has been sent."
)