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:
@@ -83,6 +83,7 @@ class BaseConfig:
|
|||||||
RATELIMIT_AUTH_TOTP_VERIFY = os.getenv("RATELIMIT_AUTH_TOTP_VERIFY", "20 per minute; 100 per hour")
|
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_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_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
|
# Logging
|
||||||
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
|
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
"""Password reset, email verification, and account activation endpoints."""
|
"""Password reset, email verification, and account activation endpoints."""
|
||||||
import logging
|
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.api.v1 import api_v1_bp
|
||||||
from gatehouse_app.extensions import limiter
|
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.utils.response import api_response
|
||||||
from gatehouse_app.services.auth_service import AuthService
|
from gatehouse_app.services.auth_service import AuthService
|
||||||
from gatehouse_app.services.notification_service import NotificationService
|
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.")
|
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"])
|
@api_v1_bp.route("/auth/activate", methods=["POST"])
|
||||||
def activate_account():
|
def activate_account():
|
||||||
import secrets
|
import secrets
|
||||||
|
|||||||
Reference in New Issue
Block a user