Skip to content

Favorites Architecture

The Favorites system enables cross-device synchronization using lightweight magic link authentication, providing seamless favorites management without requiring full user registration.

The favorites authentication flow is designed for minimal friction while maintaining security:

1. POST /api/v1/favorites/magic-link → Magic link email sent
2. User clicks email link → Browser opens /auth/verify/{token}
3. GET /api/v1/favorites/verify/{token} → Returns auth_token + favorites
4. POST /api/v1/favorites/sync → Syncs localStorage favorites
5. Future requests use Authorization: Bearer {auth_token}

Traditional registration creates friction for users who simply want to save favorites. Magic links provide:

  1. No password management — Users don’t need to remember credentials
  2. Email verification built-in — Email ownership is proven automatically
  3. Quick cross-device sync — Send link to any device to sync favorites
  4. Low security risk — Tokens only grant access to favorites, nothing else

Magic Link Token:

  • Expires after 15 minutes
  • Can only be used once
  • Links anonymous session to client account if session ID provided
  • Generates an auth token upon successful verification

Auth Token (JWT):

  • Valid for 90 days from issue
  • Can be used repeatedly for favorites operations
  • Scoped exclusively to favorites endpoints
  • Cannot access admin, agent, or other protected resources
  1. Token Expiry — Magic links expire in 15 minutes to prevent stale link abuse
  2. Single Use — Each magic link can only be verified once
  3. Rate Limiting — Maximum 3 pending links per email per 15 minutes prevents spam
  4. Signed JWTs — Auth tokens are HMAC-signed with server secret
  5. Scoped Access — Favorites tokens explicitly cannot access other API resources

The system tracks pending magic links per email address within 15-minute windows:

  • First request — Link sent immediately
  • Second request — Link sent (warning about existing links)
  • Third request — Link sent (final warning)
  • Fourth request — Rejected with 429 status

This prevents:

  • Email spam abuse
  • Accidental multiple link generation
  • Denial-of-service attacks via email endpoints

When requesting a magic link, clients can provide an X-Session-ID header. Upon token verification:

  1. Server looks up any existing anonymous session
  2. Merges anonymous favorites into the verified client account
  3. Associates session with client for future requests

This enables seamless transition from anonymous browsing to authenticated favorites sync.

Stores magic link requests and tracks usage:

ColumnPurpose
tokenUnique secure token (cryptographically random)
emailRecipient email address
session_idOptional session to link on verification
expires_atAutomatic cleanup after expiration
used_atPrevents reuse attacks
client_idSet on verification, links to Client record

Stores synced favorites with deduplication:

ColumnPurpose
client_idOwner of the favorite
listing_idMLS listing identifier
created_atWhen favorite was added

Unique constraint on (client_id, listing_id) prevents duplicate favorites.

The favorites sync uses a union merge strategy:

Server favorites: [A, B, C]
Client favorites: [C, D, E]
After sync: [A, B, C, D, E]

Prevents data loss:

  • User favorites on phone don’t disappear when syncing from desktop
  • No “last write wins” conflicts that lose data
  • Favorites are only ever added during sync, never removed

Explicit deletion:

  • Only DELETE /api/v1/favorites/{listing_id} removes favorites
  • User must intentionally remove items
  • Accidental syncs won’t lose data

Pros:

  • Simple and predictable behavior
  • No lost favorites across devices
  • Supports offline-first patterns

Cons:

  • Deleted favorites on one device won’t sync deletion to others
  • Requires explicit unfavorite action per device
  • No “tombstone” markers for deletions

For most users, the safety of never losing favorites outweighs the inconvenience of manually unfavoriting on multiple devices.

The auth token is a signed JWT with minimal claims:

{
"client_id": 123,
"brokerage_id": 1,
"email": "user@example.com",
"exp": 1735689600,
"iat": 1727913600,
"type": "favorites_auth"
}
ClaimPurpose
client_idIdentifies the client record for favorites lookup
brokerage_idMulti-tenant isolation (ensures correct data partition)
emailVerified email for user display
expAutomatic token invalidation after 90 days
typeExplicit scope marker (favorites_auth only)

The type claim is checked on every favorites endpoint to prevent token reuse for unauthorized operations.