feat(auth): add authenticated resend verification endpoint

Add new /auth/me/resend-verification endpoint that allows logged-in users
to request a new email verification link. Includes rate limiting configuration
to prevent abuse of the verification email functionality.
This commit is contained in:
2026-04-20 13:12:38 +09:30
parent 29d54ca109
commit 69f39dfa04
2 changed files with 33 additions and 1 deletions
+1
View File
@@ -83,6 +83,7 @@ class BaseConfig:
RATELIMIT_AUTH_TOTP_VERIFY = os.getenv("RATELIMIT_AUTH_TOTP_VERIFY", "20 per minute; 100 per hour")
RATELIMIT_AUTH_FORGOT_PASSWORD = os.getenv("RATELIMIT_AUTH_FORGOT_PASSWORD", "5 per minute; 20 per hour")
RATELIMIT_AUTH_RESET_PASSWORD = os.getenv("RATELIMIT_AUTH_RESET_PASSWORD", "10 per minute; 30 per hour")
RATELIMIT_AUTH_RESEND_VERIFICATION = os.getenv("RATELIMIT_AUTH_RESEND_VERIFICATION", "5 per minute; 20 per hour")
# Logging
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
+32 -1
View File
@@ -1,8 +1,9 @@
"""Password reset, email verification, and account activation endpoints."""
import logging
from flask import request, current_app
from flask import request, current_app, g
from gatehouse_app.api.v1 import api_v1_bp
from gatehouse_app.extensions import limiter
from gatehouse_app.utils.decorators import login_required
from gatehouse_app.utils.response import api_response
from gatehouse_app.services.auth_service import AuthService
from gatehouse_app.services.notification_service import NotificationService
@@ -151,6 +152,36 @@ def resend_verification():
return api_response(data={}, message="If an account exists for this email and is not yet verified, you will receive a verification link shortly.")
@api_v1_bp.route("/auth/me/resend-verification", methods=["POST"])
@limiter.limit(lambda: current_app.config["RATELIMIT_AUTH_RESEND_VERIFICATION"])
@login_required
def resend_verification_authenticated():
from gatehouse_app.models import EmailVerificationToken
user = g.current_user
if not user.email_verified:
try:
verify_token = EmailVerificationToken.generate(user_id=user.id)
app_url = current_app.config.get("APP_URL", "http://localhost:8080")
verify_link = f"{app_url}/verify-email?token={verify_token.token}"
email_body = build_email_verification_html(
user_name=user.full_name or user.email,
verify_link=verify_link,
expiry_hours=24,
)
NotificationService._send_email_async(
to_address=user.email,
subject="Verify your Secuird email address",
body=f"Verify your Secuird email: {verify_link}",
html_body=email_body,
)
_logger.info(f"Verification email sent for authenticated user {user.id}")
except Exception as exc:
_logger.exception(f"Error sending verification email: {exc}")
return api_response(data={}, message="If your email is not yet verified, you will receive a verification link shortly.")
@api_v1_bp.route("/auth/activate", methods=["POST"])
def activate_account():
import secrets