Update: 2026-07-30 02:27:45
This commit is contained in:
@@ -40,7 +40,12 @@ if (!$user || !password_verify($password, $user['password_hash'])) {
|
||||
}
|
||||
|
||||
$deviceId = $data['device_id'] ?? null;
|
||||
$isReviewer = (strtolower($email) === 'reviewer@musadaq.jo');
|
||||
|
||||
// App-store reviewer account skips the WhatsApp OTP step (reviewers have no
|
||||
// access to the registered phone). Configured via .env so the exception is not
|
||||
// baked into the source, and disabled entirely when the var is unset.
|
||||
$reviewerEmail = strtolower(trim((string)env('REVIEWER_EMAIL', '')));
|
||||
$isReviewer = $reviewerEmail !== '' && strtolower($email) === $reviewerEmail;
|
||||
|
||||
if ($deviceId && !$isReviewer) {
|
||||
// Generate and send WhatsApp OTP
|
||||
@@ -127,7 +132,9 @@ if ($deviceId) {
|
||||
$deviceName,
|
||||
$data['platform'] ?? 'web',
|
||||
$data['app_version'] ?? '1.0.0',
|
||||
password_hash($deviceSecret, PASSWORD_DEFAULT),
|
||||
// Stored encrypted, NOT bcrypt-hashed: the server must be able to
|
||||
// recompute the client's HMAC signature from this same secret.
|
||||
\App\Core\Encryption::encrypt($deviceSecret),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -152,11 +159,28 @@ $payload = [
|
||||
|
||||
$token = JWT::encode($payload, $secret);
|
||||
|
||||
// 5. Update Refresh Token (Hashed before storage for security)
|
||||
// 5. Issue Refresh Token (hashed before storage).
|
||||
//
|
||||
// Mobile tokens are stored PER DEVICE. users.refresh_token_hash is a single
|
||||
// column, so writing there logged the user out of every other device silently.
|
||||
$refreshToken = bin2hex(random_bytes(32));
|
||||
$refreshTokenHash = hash('sha256', $refreshToken);
|
||||
$stmt = $db->prepare("UPDATE users SET refresh_token_hash = ?, last_login_at = NOW() WHERE id = ?");
|
||||
$stmt->execute([$refreshTokenHash, $user['id']]);
|
||||
$refreshTtlDays = $deviceId ? 60 : 7;
|
||||
$refreshExpiresAt = date('Y-m-d H:i:s', time() + ($refreshTtlDays * 24 * 3600));
|
||||
|
||||
if ($deviceId) {
|
||||
$stmt = $db->prepare("
|
||||
UPDATE user_devices
|
||||
SET refresh_token_hash = ?, refresh_expires_at = ?, last_seen_at = NOW()
|
||||
WHERE user_id = ? AND device_fingerprint = ?
|
||||
");
|
||||
$stmt->execute([$refreshTokenHash, $refreshExpiresAt, $user['id'], $deviceId]);
|
||||
|
||||
$db->prepare("UPDATE users SET last_login_at = NOW() WHERE id = ?")->execute([$user['id']]);
|
||||
} else {
|
||||
$stmt = $db->prepare("UPDATE users SET refresh_token_hash = ?, last_login_at = NOW() WHERE id = ?");
|
||||
$stmt->execute([$refreshTokenHash, $user['id']]);
|
||||
}
|
||||
|
||||
// 6. Secure Refresh Token delivery via HttpOnly Cookie (for web)
|
||||
if (!$deviceId) {
|
||||
|
||||
Reference in New Issue
Block a user