prepare('SELECT * FROM auth_users WHERE email = ? OR username = ? LIMIT 1'); $stmt->execute([$login, $login]); $user = $stmt->fetch(); if (!$user || !password_verify($pass, (string) $user['password'])) { json_error(401, 'Invalid credentials'); } // Distinct code so the app opens the verification screen instead of blaming the password. if (empty($user['email_verified_at'])) { json_error(403, 'E-mail is not verified', null, 'email_not_verified'); } $plain = bin2hex(random_bytes(32)); $config = require __DIR__ . '/config.php'; $expires = (new DateTimeImmutable("+{$config['token_ttl_days']} days"))->format('Y-m-d H:i:s'); $pdo->prepare( 'INSERT INTO api_tokens (user_id, name, token, expires_at, created_at) VALUES (?, ?, ?, ?, NOW())' )->execute([$user['id'], $device, hash('sha256', $plain), $expires]); json_data([ 'token' => $plain, 'user' => present_user($user), ]);