feat(auth): switch signup to OTP verification flow

Replace legacy signup with start/verify/resend endpoints, add OTP-focused mail templates and auth rate limits, and align compose/env/runbook for local self-hosted Supabase OTP behavior.
This commit is contained in:
qzl
2026-02-25 13:34:02 +08:00
parent 02e5e52e1f
commit 1cc8fa1abf
16 changed files with 707 additions and 112 deletions
+44 -5
View File
@@ -6,15 +6,20 @@ from fastapi import APIRouter, Depends, Response
from fastapi import HTTPException
from core.auth.models import CurrentUser
from v1.auth.rate_limit import enforce_rate_limit
from v1.auth.dependencies import get_auth_service
from v1.profile.dependencies import get_current_user
from v1.auth.schemas import (
AuthResendCodeResponse,
AuthSignupStartResponse,
AuthTokenResponse,
AuthUserByEmailResponse,
LoginRequest,
LogoutRequest,
RefreshRequest,
SignupRequest,
SignupResendRequest,
SignupStartRequest,
SignupVerifyRequest,
)
from v1.auth.service import AuthService
@@ -22,12 +27,46 @@ from v1.auth.service import AuthService
router = APIRouter(prefix="/auth", tags=["auth"])
@router.post("/signup", response_model=AuthTokenResponse)
async def signup(
payload: SignupRequest,
@router.post("/signup/start", response_model=AuthSignupStartResponse, status_code=202)
async def signup_start(
payload: SignupStartRequest,
service: AuthService = Depends(get_auth_service),
) -> AuthSignupStartResponse:
await enforce_rate_limit(
scope="signup_start",
identifier=payload.email,
limit=5,
window_seconds=60,
)
return await service.signup_start(payload)
@router.post("/signup/verify", response_model=AuthTokenResponse)
async def signup_verify(
payload: SignupVerifyRequest,
service: AuthService = Depends(get_auth_service),
) -> AuthTokenResponse:
return await service.signup(payload)
await enforce_rate_limit(
scope="signup_verify",
identifier=payload.email,
limit=10,
window_seconds=600,
)
return await service.signup_verify(payload)
@router.post("/signup/resend", response_model=AuthResendCodeResponse)
async def signup_resend(
payload: SignupResendRequest,
service: AuthService = Depends(get_auth_service),
) -> AuthResendCodeResponse:
await enforce_rate_limit(
scope="signup_resend",
identifier=payload.email,
limit=3,
window_seconds=60,
)
return await service.signup_resend(payload)
@router.post("/login", response_model=AuthTokenResponse)