Change 'Rack' to 'Bin'
This commit is contained in:
@@ -23,8 +23,9 @@
|
||||
* b. The elapsed time since otpTime is ≤ 5 minutes.
|
||||
* Fail either → return "Wrong OTP! Please try again."
|
||||
* 5. On success:
|
||||
* a. Check session_token in DB — if non-NULL, another session is active;
|
||||
* reject login with "account already logged in" message.
|
||||
* a. Force-replace session_token in DB — writing a new token invalidates
|
||||
* any prior session (old device gets kicked out by db_auth.php on its
|
||||
* next request). No block: password + OTP is full 2FA proof of identity.
|
||||
* b. session_regenerate_id(true) — prevents session fixation attack by
|
||||
* issuing a new session ID and deleting the old one.
|
||||
* c. Generate a fresh CSRF token and store in session.
|
||||
@@ -105,31 +106,28 @@ if (empty($_SESSION['skip_otp'])) {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Step 4b: Claim session atomically ─────────────────────────────────────────
|
||||
// The WHERE clause is the concurrency gate: only a free or stale token can be
|
||||
// replaced. session_last_seen is set immediately so a fresh login is live before
|
||||
// the first authenticated heartbeat in db_auth.php.
|
||||
// ── Step 4b: Claim session — always replace existing token ────────────────────
|
||||
// Password + OTP is full 2FA proof of identity, so we always grant the login.
|
||||
// Writing a new token here also invalidates any prior session: db_auth.php
|
||||
// compares session_token in the DB to the one stored in the PHP session, so the
|
||||
// old device is kicked out on its next request. This also fixes the case where
|
||||
// a PHP session expired without clearing the DB token, which would otherwise
|
||||
// permanently block re-login.
|
||||
$session_token = bin2hex(random_bytes(32));
|
||||
$sth_claim = $pdo1->prepare(
|
||||
"UPDATE user
|
||||
SET session_token = :token,
|
||||
session_token_at = NOW(),
|
||||
SET session_token = :token,
|
||||
session_token_at = NOW(),
|
||||
session_last_seen = NOW()
|
||||
WHERE user_id = :uid
|
||||
AND (
|
||||
session_token IS NULL
|
||||
OR session_last_seen IS NULL
|
||||
OR TIMESTAMPDIFF(SECOND, session_last_seen, NOW()) >= :gc_maxlifetime
|
||||
)"
|
||||
WHERE user_id = :uid"
|
||||
);
|
||||
$sth_claim->execute([
|
||||
':token' => $session_token,
|
||||
':uid' => $user_id,
|
||||
':gc_maxlifetime' => (int)ini_get('session.gc_maxlifetime'),
|
||||
':token' => $session_token,
|
||||
':uid' => $user_id,
|
||||
]);
|
||||
|
||||
if ($sth_claim->rowCount() !== 1) {
|
||||
$answer["message"] = "This account is currently logged in on another device. Please wait for the other session to end.";
|
||||
$answer["message"] = "Login failed: user record not found.";
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user