Sign-in & Sessions

Everything that happens after registration: the sign-in screen, two-factor challenge, forgot-password recovery, magic-link entry, "new device detected" alerts, and active session management. The whole post-registration auth surface — owner side and public side share the same components, only the routing changes.

UI — sign-in screen (admin console)

app.abc.com/signin
A
ABc
Welcome back
Sign in to manage your properties.
or
Don't have an account? Create one
Park View Hotel
"Direct bookings up 38 % since we switched to ABc."

Sign-in flow

  1. User submits credentials

    Client validates email format and minimum password length, then POSTs to /api/auth/signin. Honeypot field included, hCaptcha token attached if risk score is elevated.

  2. Server verifies password

    Argon2id verify against stored hash. Failed attempts go to a per-account rate limiter (5 / 10 min) and per-IP limiter (20 / hour).

  3. Risk assessment

    Compare IP geo, device fingerprint and time-of-day against the user's history. High deviation → 2FA required even if 2FA isn't permanently enabled.

  4. If 2FA is enabled or required

    Return { status: "2fa_required", challenge_token }. Client renders the 2FA screen; no session cookie issued yet.

  5. Issue session

    Set HTTP-only secure session cookie (15-min access + 30-day refresh). Audit-log auth.signed_in with device + IP.

  6. Redirect

    Owners → property dashboard. Public users → their previous URL or home.

UI — two-factor challenge

app.abc.com/signin/2fa
Verify it's you
Enter the 6-digit code from your authenticator app.
Code expires in 28 seconds
Can't access your authenticator?
Use an SMS code sent to your verified phone, or one of your 8 recovery codes. If you've lost everything, contact support.

UI — forgot password (3 steps)

/forgot · step 1
Reset your password
We'll email you a one-time link.
/forgot/sent · step 2
Check your inbox
A link has been sent to rohan@parkviewhotel.in — valid for 60 minutes.
Didn't get it? Check spam, or resend in 28s.
/reset?t=xy · step 3
Set a new password
10+ chars · mixed case · 1 number
Strong password

UI — magic link sign-in

inbox · email preview
A
ABc
Sign-in link
Sign in with one tap
Hi Rohan — tap the button below to sign in to your ABc dashboard. This link expires in 10 minutes and can be used once.
Sign in →
Didn't request this? You can safely ignore this email — no one can sign in unless they click the link. If this happens repeatedly, secure your account by changing your password.
Requested from: Chrome on macOS · Manali, IN · 25 May 2026, 18:42 IST

UI — "new device detected"

If a sign-in succeeds from an IP/device the user hasn't seen before, we send an alert and surface a banner on next sign-in.

app.abc.com — after sign-in
New device sign-in detected
A sign-in just occurred from Chrome on Windows in Delhi, IN. If this was you, you can dismiss this. If not, sign out everywhere and change your password immediately.

Active sessions screen

Every signed-in device is visible to the user under Settings → Security → Sessions. The list updates every 30 seconds. Manual sign-out is one click. Sign-out-everywhere bumps the JWT version and kills everything including the current tab.

app.abc.com/settings/security
Active sessions
3 devices signed in · last cleared 12 days ago
DeviceLocation · IPLast active
Chrome on macOS
Sonoma 14.4
Manali, IN
103.21.x.x
Just now Current
Safari on iPhone
iOS 18.1
Manali, IN
103.21.x.x
2 hours ago
Chrome on Windows New
Windows 11
Delhi, IN
122.176.x.x
3 days ago

API contract

POST /api/auth/signin
// Request
{
  "email": "rohan@parkviewhotel.in",
  "password": "••••••••••",
  "remember": true,
  "device_fingerprint": "d4f8a…",
  "hcaptcha_token": "…"           // only when risk score > 0.5
}

// Response — 200 (no 2FA)
{
  "status": "signed_in",
  "user": { "id": "usr_…", "role": "owner" },
  "redirect": "/properties"
}

// Response — 200 (2FA required)
{
  "status": "2fa_required",
  "challenge_token": "ch_01HW…",    // short-lived, 10 min
  "method": "totp",
  "fallback_methods": ["sms", "recovery_code"]
}

// Response — 401 (bad credentials)
{ "error": "invalid_credentials", "message": "Wrong email or password." }

// Response — 429 (rate limited)
{ "error": "rate_limited", "retry_after": 600 }

Cookies & tokens

CookiePurposeLifetimeFlags
abc_sessionShort-lived access token (JWT)15 minutesHttpOnly · Secure · SameSite=Lax
abc_refreshRefresh token for silent re-auth30 days (90 if "remember")HttpOnly · Secure · SameSite=Strict
abc_deviceDevice fingerprint, low-PII1 yearHttpOnly · Secure · SameSite=Lax
abc_csrfDouble-submit CSRF tokenSessionSecure · SameSite=Strict (readable by JS)

Why we don't show a generic "invalid credentials" error

Email enumeration is the threat — not weak passwords.

If we said "no account with that email" vs "wrong password", an attacker could discover whether your email is registered. We always return the same friendly "Wrong email or password" string, regardless of which is wrong, with a consistent response time (we run the Argon2id hash even for non-existent emails to avoid timing leaks).

Edge cases handled

  • Account locked after 10 failed attempts — auto-unlock after 30 minutes, but user is emailed.
  • 2FA recovery code use — single-use, the user gets a fresh batch of 8 codes after using one.
  • Magic-link reuse attempt — second click returns "link already used" page, prompts sign-in normally.
  • Sign-in from suspended account — credentials accepted but user is shown the "account on hold" screen, not the dashboard.
  • Concurrent sign-in from two devices — fine, both get sessions; user can see both in the sessions screen.
  • Stolen refresh-token detection — refresh tokens are rotated on every use; reusing an old one signs the user out everywhere and alerts.