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
|
// Handle different update scopes
|
||||||
if (updateScope === "current" || !updateScope) {
|
if (updateScope === "current" || !updateScope) {
|
||||||
// Update only the current location (default behavior)
|
// Update only the current location (default behavior)
|
||||||
@@ -194,7 +208,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
|||||||
tenantTown: tenantTown || null,
|
tenantTown: tenantTown || null,
|
||||||
autoBillFwd: autoBillFwd || false,
|
autoBillFwd: autoBillFwd || false,
|
||||||
tenantEmail: tenantEmail || null,
|
tenantEmail: tenantEmail || null,
|
||||||
tenantEmailStatus: tenantEmailStatus as EmailStatus || EmailStatus.Unverified,
|
tenantEmailStatus: finalEmailStatus,
|
||||||
billFwdStrategy: billFwdStrategy || "when-payed",
|
billFwdStrategy: billFwdStrategy || "when-payed",
|
||||||
rentDueNotification: rentDueNotification || false,
|
rentDueNotification: rentDueNotification || false,
|
||||||
rentDueDay: rentDueDay || null,
|
rentDueDay: rentDueDay || null,
|
||||||
@@ -226,7 +240,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
|||||||
tenantTown: tenantTown || null,
|
tenantTown: tenantTown || null,
|
||||||
autoBillFwd: autoBillFwd || false,
|
autoBillFwd: autoBillFwd || false,
|
||||||
tenantEmail: tenantEmail || null,
|
tenantEmail: tenantEmail || null,
|
||||||
tenantEmailStatus: tenantEmailStatus as EmailStatus || EmailStatus.Unverified,
|
tenantEmailStatus: finalEmailStatus,
|
||||||
billFwdStrategy: billFwdStrategy || "when-payed",
|
billFwdStrategy: billFwdStrategy || "when-payed",
|
||||||
rentDueNotification: rentDueNotification || false,
|
rentDueNotification: rentDueNotification || false,
|
||||||
rentDueDay: rentDueDay || null,
|
rentDueDay: rentDueDay || null,
|
||||||
@@ -251,7 +265,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
|
|||||||
tenantTown: tenantTown || null,
|
tenantTown: tenantTown || null,
|
||||||
autoBillFwd: autoBillFwd || false,
|
autoBillFwd: autoBillFwd || false,
|
||||||
tenantEmail: tenantEmail || null,
|
tenantEmail: tenantEmail || null,
|
||||||
tenantEmailStatus: tenantEmailStatus as EmailStatus || EmailStatus.Unverified,
|
tenantEmailStatus: finalEmailStatus,
|
||||||
billFwdStrategy: billFwdStrategy || "when-payed",
|
billFwdStrategy: billFwdStrategy || "when-payed",
|
||||||
rentDueNotification: rentDueNotification || false,
|
rentDueNotification: rentDueNotification || false,
|
||||||
rentDueDay: rentDueDay || null,
|
rentDueDay: rentDueDay || null,
|
||||||
|
|||||||
Reference in New Issue
Block a user