New-login security alerts ("we noticed a new login to your account").
On every login (PhoenixKitWeb.Users.Auth.log_in_user/3), the request's
(ip_address, user_agent_hash) pair is checked against
PhoenixKit.Users.Auth.KnownDevice rows for that user. An unrecognized
pair is a new device: it's persisted and a user.new_login_detected
activity entry is logged either way — but the email
(PhoenixKit.Users.Auth.UserNotifier.deliver_new_login_alert/2, gated
behind new_login_alert_enabled) and the in-app notification are skipped
when this is the very first KnownDevice row the account has ever had.
That first-device skip exists because registration ends by logging the new
user in through this exact path (log_in_user/3), and an account with no
device history yet cannot help but treat its own signup as "a new device" —
without it, every signup on an installation with alerts on immediately
received a "we noticed a new login" security email about the login it just
performed to finish registering. The device is still recorded (so the
second login, from anywhere else, correctly reads as new), and the
activity entry still logs for the audit trail — only the two
reader-facing alarms are suppressed.
A recognized pair just bumps last_seen_at — no alert, no email.
Sends synchronously (matching every other PhoenixKit auth email — confirmation, password reset, magic link — none of which are queued through Oban): a "new device" login is inherently rare per user (every subsequent login from the same device is silent), so the odd extra round-trip on a first-time login doesn't justify background-job infrastructure this feature would otherwise be the only user of. A send failure is logged and swallowed — it must never block sign-in.
Summary
Functions
Records a login from conn for user, alerting on a new device.
Whether new-login alerts are enabled (setting new_login_alert_enabled,
default false).
Best-effort "City, Country" string for ip_address, or nil.
Functions
@spec check(map(), Plug.Conn.t()) :: :ok
Records a login from conn for user, alerting on a new device.
No-ops entirely (no DB write, no email) when the feature is disabled. Never raises — a failure here must never block sign-in.
@spec enabled?() :: boolean()
Whether new-login alerts are enabled (setting new_login_alert_enabled,
default false).
Best-effort "City, Country" string for ip_address, or nil.
Never raises; a lookup failure (disabled, rate-limited, invalid IP) just means the alert email omits the location line.