security: add server-side validation for email status transitions
Implement strict validation to prevent unauthorized email status changes: - Force status to Unverified when email address changes - Only allow client to reset status to Unverified (via reset button) - Block client from upgrading status (Unverified→Verified, etc.) - All status upgrades must happen server-side via verification links This prevents attackers from: - Submitting new emails with fake "verified" status - Bypassing email verification by modifying client requests - Escalating email status without proper verification flow 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -176,6 +176,20 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
||||
};
|
||||
}
|
||||
|
||||
// SECURITY: Validate email status transitions
|
||||
// - If email changed: force to Unverified (prevents spoofing verified status)
|
||||
// - If email unchanged: only allow client to reset to Unverified (via reset button)
|
||||
// All other status transitions (Unverified→VerificationPending, VerificationPending→Verified)
|
||||
// must happen server-side through other mechanisms (email verification links, etc.)
|
||||
const emailHasChanged = currentLocation.tenantEmail !== (tenantEmail || null);
|
||||
const clientWantsToReset = tenantEmailStatus === EmailStatus.Unverified;
|
||||
|
||||
const finalEmailStatus = emailHasChanged
|
||||
? EmailStatus.Unverified // Email changed: force reset
|
||||
: clientWantsToReset
|
||||
? EmailStatus.Unverified // Client initiated reset: allow it
|
||||
: (currentLocation.tenantEmailStatus || EmailStatus.Unverified); // Otherwise: keep current status
|
||||
|
||||
// Handle different update scopes
|
||||
if (updateScope === "current" || !updateScope) {
|
||||
// Update only the current location (default behavior)
|
||||
@@ -194,7 +208,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
||||
tenantTown: tenantTown || null,
|
||||
autoBillFwd: autoBillFwd || false,
|
||||
tenantEmail: tenantEmail || null,
|
||||
tenantEmailStatus: tenantEmailStatus as EmailStatus || EmailStatus.Unverified,
|
||||
tenantEmailStatus: finalEmailStatus,
|
||||
billFwdStrategy: billFwdStrategy || "when-payed",
|
||||
rentDueNotification: rentDueNotification || false,
|
||||
rentDueDay: rentDueDay || null,
|
||||
@@ -226,7 +240,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
||||
tenantTown: tenantTown || null,
|
||||
autoBillFwd: autoBillFwd || false,
|
||||
tenantEmail: tenantEmail || null,
|
||||
tenantEmailStatus: tenantEmailStatus as EmailStatus || EmailStatus.Unverified,
|
||||
tenantEmailStatus: finalEmailStatus,
|
||||
billFwdStrategy: billFwdStrategy || "when-payed",
|
||||
rentDueNotification: rentDueNotification || false,
|
||||
rentDueDay: rentDueDay || null,
|
||||
@@ -251,7 +265,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
||||
tenantTown: tenantTown || null,
|
||||
autoBillFwd: autoBillFwd || false,
|
||||
tenantEmail: tenantEmail || null,
|
||||
tenantEmailStatus: tenantEmailStatus as EmailStatus || EmailStatus.Unverified,
|
||||
tenantEmailStatus: finalEmailStatus,
|
||||
billFwdStrategy: billFwdStrategy || "when-payed",
|
||||
rentDueNotification: rentDueNotification || false,
|
||||
rentDueDay: rentDueDay || null,
|
||||
|
||||
Reference in New Issue
Block a user