Jump to content

Recommended Posts

 

I want to make it so when the email and password and remember_me cookies expire the user is logged out but only if they originally clicked remember me, if they didn't nothing will happen. how do I go about doing that? when I enter index and the cookies expired if I clicked remember me before it then it redirects to login page. if you didn't click remember me, you don't redirect anywhere and no cookies are there.

also want to make the cookie password into a hashed password or token. how can I do this?
how do I alter my already written code to do this?

 

<?php
session_start();
require_once 'config.php'; 

if (!isset($_SESSION['email']) && isset($_COOKIE['email'], $_COOKIE['password'], $_COOKIE['remember_me'])) {
    $email = $_COOKIE['email'];
    $password = $_COOKIE['password'];

    $stmt = $conn->prepare("SELECT * FROM users WHERE email = ?");
    $stmt->bind_param("s", $email);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($result->num_rows > 0) {
        $user = $result->fetch_assoc();
        if (password_verify($password, $user['password'])) {
            $_SESSION['username'] = $user['username'];
            $_SESSION['email'] = $user['email'];
            $_SESSION['role'] = $user['role'];
            $_SESSION['location'] = $user['location'];

            if ($user['role'] === 'admin') {
                header("Location: admin.php");
            } else {
                header("Location: index.php");
            }
            exit();
        }
    }
    setcookie('remember_me', '', time() - 3600, "/");
    setcookie('email', '', time() - 3600, "/");
    setcookie('password', '', time() - 3600, "/");
    $stmt->close();
}


$errors = [
    'login' => $_SESSION['login_error'] ?? '',
    'register' => $_SESSION['register_error'] ?? ''
];
$successMessage = $_SESSION['register_success'] ?? '';
$activeForm = $_SESSION['active_form'] ?? 'login';
$loginAttempts = $_SESSION['login_attempts'] ?? 0;
$lockoutTime = $_SESSION['lockout_time'] ?? 0;

unset($_SESSION['login_error'], $_SESSION['register_error'], $_SESSION['register_success'], $_SESSION['active_form']);

function showError($error) {
    return !empty($error) ? "<p class='error-message'>" . htmlspecialchars($error) . "</p>" : "";
}

function showSuccess($message) {
    return !empty($message) ? "<p class='success-message'>" . htmlspecialchars($message) . "</p>" : "";
}

function isActiveForm($formName, $activeForm) {
    return $formName === $activeForm ? 'active' : '';
}

$currentTime = time();
$remainingLockoutTime = 0;
$isLocked = false;

if ($loginAttempts >= 3) {
    if (($currentTime - $lockoutTime) < 40) {
        $isLocked = true;
        $remainingLockoutTime = 40 - ($currentTime - $lockoutTime);
    } else {
        $_SESSION['login_attempts'] = 0;
        $_SESSION['lockout_time'] = 0;
    }
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ALnasser | Ticketing System</title>
    <link rel="icon" type="image/x-icon" href="alnasser.png">
    <link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="container">
    <div class="form-box <?= isActiveForm('login', $activeForm); ?>" id="login-form">
        <form action="login_register.php" method="post">
            <center><img width="30%" height="auto" src="alnasser_nobg.png" alt="ALnasser Logo"></center>
            <h2>Login</h2>
            <?= showError($errors['login']); ?>
            <button type="button" class="sso-button" onclick="window.location.href='windows_login.php'">
                Sign in with Windows Domain Account
            </button>
            <div class="divider"><span class="divider-text">OR</span></div>

            <input type="email" name="email" placeholder="Email" required>
            <input type="password" name="password" placeholder="Password" required>

            <div class="remember-me">
                <input type="checkbox" id="remember_me" name="remember_me">
                <label for="remember">Remember me for 30 days</label>
            </div>

            <?php if ($isLocked): ?>
                <div id="countdown">Too many failed attempts. Please try again in <span id="time"></span> seconds.</div>
                <button type="submit" name="login" disabled style="cursor: not-allowed; background-color: #ccc;">Login</button>
            <?php else: ?>
                <button type="submit" name="login">Login</button>
            <?php endif; ?>

            <p class="form-footer">Don't have an account? <a href="#" onclick="showForm('register-form')">Register</a></p>
        </form>
    </div>

    <div class="form-box <?= isActiveForm('register', $activeForm); ?>" id="register-form">
        <form action="login_register.php" method="post">
            <center><img width="30%" height="auto" src="alnasser_nobg.png" alt="ALnasser Logo"></center>
            <h2>Register</h2>
            <?= showError($errors['register']); ?>
            <?= showSuccess($successMessage); ?>

            <input type="text" name="username" placeholder="Username" required>
            <input type="email" name="email" placeholder="Email" pattern="[a-zA-Z0-9._%+-]+@alnasser\.eg$" required>
            <input type="password" name="password" placeholder="Password" required>
            <select name="role" required>
                <option value="">--Select Role--</option>
                <option value="user">User</option>
                <option value="admin">Admin</option>
                <option value="technician">Technician</option>
            </select>
            <select name="location" required>
                <option value="">--Select Location--</option>
                <option value="Asiout">Asiout</option>
                <option value="Zizinia">Zizinia</option>
                <option value="Aswan">Aswan</option>
                <option value="Helwan">Helwan</option>
                <option value="Menia">Menia</option>
                <option value="Mokattam">Mokattam</option>
                <option value="Arcadia">Arcadia</option>
                <option value="October">October</option>
                <option value="Tagamoa">Tagamoa</option>
                <option value="Maadi">Maadi</option>
                <option value="Heliopolis">Heliopolis</option>
                <option value="Nasr city">Nasr city</option>
                <option value="Obour">Obour</option>
                <option value="Qena">Qena</option>
                <option value="Smouha">Smouha</option>
                <option value="Haram">Haram</option>
                <option value="Sohag1">Sohag1</option>
                <option value="Bani Suef">Bani Suef</option>
                <option value="Mohandseen">Mohandseen</option>
                <option value="Tanta">Tanta</option>
                <option value="Mahalla">Mahalla</option>
                <option value="Zaqaziq">Zaqaziq</option>
                <option value="Shebeen">Shebeen</option>
                <option value="Qusseya">Qusseya</option>
                <option value="Mansoura2">Mansoura2</option>
                <option value="Luxor">Luxor</option>
                <option value="Damanhor">Damanhor</option>
                <option value="Hadayek">Hadayek</option>
                <option value="Agami">Agami</option>
                <option value="Suez">Suez</option>
                <option value="Fisal">Fisal</option>
                <option value="ismailia">ismailia</option>
                <option value="Mansoura 3">Mansoura 3</option>
                <option value="Abas el3qad">Abas el3qad</option>
                <option value="mohy eldeen">mohy eldeen</option>
                <option value="Sohag2">Sohag2</option>
                <option value="Zaharaa El-Maadi">Zaharaa El-Maadi</option>
                <option value="Gesr Al-Suez">Gesr Al-Suez</option>
                <option value="Shoubra">Shoubra</option>
                <option value="Fayoum">Fayoum</option>
                <option value="Hurghada">Hurghada</option>
                <option value="Sharm ElSheikh">Sharm ElSheikh</option>
                <option value="Mashaal">Mashaal</option>
                <option value="Victoria">Victoria</option>
                <option value="Al Rehab">Al Rehab</option>
                <option value="Madinaty">Madinaty</option>
                <option value="Mall of Egypt">Mall of Egypt</option>
                <option value="Gardenia">Gardenia</option>
                <option value="Tanta 2">Tanta 2</option>
                <option value="Port Said">Port Said</option>
                <option value="Town Center Mall">Town Center Mall</option>
                <option value="Office">Office</option>
                <option value="Online">Online</option>
            </select>
            <button type="submit" name="register">Register</button>
            <p class="form-footer">Already have an account? <a href="#" onclick="showForm('login-form')">Login</a></p>
        </form>
    </div>
</div>
<script src="script.js"></script>
<script>
    <?php if ($isLocked): ?>
        let remainingTime = <?= $remainingLockoutTime ?>;
        const countdownElement = document.getElementById('time');
        function updateCountdown() {
            if (remainingTime > 0) {
                countdownElement.textContent = remainingTime;
                remainingTime--;
                setTimeout(updateCountdown, 1000);
            } else {
                window.location.reload();
            }
        }
        updateCountdown();
    <?php endif; ?>

    function showForm(formId) {
        document.querySelectorAll('.form-box').forEach(box => box.classList.remove('active'));
        document.getElementById(formId).classList.add('active');
    }

    window.onload = function() {
        const activeFormId = '<?= htmlspecialchars($activeForm) ?>-form';
        showForm(activeFormId);
    };
</script>
</body>
</html>
<?php
session_start();
require_once 'config.php';


if (isset($_POST['register'])) {
    $username = trim($_POST['username']);
    $email = trim($_POST['email']);
    $password_raw = $_POST['password'];
    $role = $_POST['role'];
    $location = $_POST['location'];

    if (!preg_match('/^[a-zA-Z0-9_]+$/', $username)) {
        $_SESSION['register_error'] = 'Username can only contain letters, numbers, and underscores.';
        $_SESSION['active_form'] = 'register';
        header("Location: login&signup.php");
        exit();
    }

    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $_SESSION['register_error'] = 'Invalid email format.';
        $_SESSION['active_form'] = 'register';
        header("Location: login&signup.php");
        exit();
    }

    if (!preg_match('/@alnasser\.eg$/', $email)) {
        $_SESSION['register_error'] = 'Only @alnasser.eg email addresses are allowed.';
        $_SESSION['active_form'] = 'register';
        header("Location: login&signup.php");
        exit();
    }

    if (strlen($password_raw) < 8 || !preg_match('/[A-Za-z]/', $password_raw) || !preg_match('/[0-9]/', $password_raw) || !preg_match('/[^A-Za-z0-9]/', $password_raw)) {
        $_SESSION['register_error'] = 'Password must be at least 8 characters long and include letters, numbers, and symbols.';
        $_SESSION['active_form'] = 'register';
        header("Location: login&signup.php");
        exit();
    }

    $password_hashed = password_hash($password_raw, PASSWORD_DEFAULT);

    $stmt = $conn->prepare("SELECT email FROM users WHERE email = ?");
    $stmt->bind_param("s", $email);
    $stmt->execute();
    $checkEmail = $stmt->get_result();

    if ($checkEmail->num_rows > 0) {
        $_SESSION['register_error'] = 'Email is already registered.';
        $_SESSION['active_form'] = 'register';
    } else {
        $stmt = $conn->prepare("INSERT INTO users (username, email, password, role, location) VALUES (?, ?, ?, ?, ?)");
        $stmt->bind_param("sssss", $username, $email, $password_hashed, $role, $location);

        if ($stmt->execute()) {
            $_SESSION['active_form'] = 'login';
            $_SESSION['register_success'] = 'Registration successful! Please login.';
        } else {
            error_log("Registration failed: " . $stmt->error);
            $_SESSION['register_error'] = 'Registration failed. Please try again.';
            $_SESSION['active_form'] = 'register';
        }
    }

    $stmt->close();
    $conn->close();
    header("Location: login&signup.php");
    exit();
}

if (isset($_POST['login'])) {
    $email = trim($_POST['email']);
    $password = $_POST['password'];

    $loginAttempts = $_SESSION['login_attempts'] ?? 0;
    $lockoutTime = $_SESSION['lockout_time'] ?? 0;
    $currentTime = time();

    if ($loginAttempts >= 3 && ($currentTime - $lockoutTime < 40)) {
        $_SESSION['login_error'] = 'Account locked due to too many failed attempts. Please wait.';
        $_SESSION['active_form'] = 'login';
        header("Location: login&signup.php");
        exit();
    }

    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $_SESSION['login_error'] = 'Invalid email format.';
        $_SESSION['active_form'] = 'login';
        header("Location: login&signup.php");
        exit();
    }

    if (!preg_match('/@alnasser\.eg$/', $email)) {
        $_SESSION['login_error'] = 'Only @alnasser.eg email addresses are allowed.';
        $_SESSION['active_form'] = 'login';
        header("Location: login&signup.php");
        exit();
    }

    $stmt = $conn->prepare("SELECT * FROM users WHERE email = ?");
    $stmt->bind_param("s", $email);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($result->num_rows > 0) {
        $user = $result->fetch_assoc();
        if (password_verify($password, $user['password'])) {

            $_SESSION['username'] = $user['username'];
            $_SESSION['email'] = $user['email'];
            $_SESSION['role'] = $user['role'];
            $_SESSION['location'] = $user['location'];

            $_SESSION['login_attempts'] = 0;
            $_SESSION['lockout_time'] = 0;

            if (!empty($_POST['remember_me'])) {
                setcookie('remember_me', '1', time() + (60 * 60 * 24 * 30), "/");
                setcookie('email', $_POST['email'], time() + (60* 60 * 24 * 30), "/");
                setcookie('password', $_POST['password'], time() + (60* 60 * 24 * 30), "/");

            } else {
                setcookie('remember_me', '', time() - 3600, "/");
                setcookie('email', '', time() - 3600, "/");
                setcookie('password', '', time() - 3600, "/");
            }


            $stmt->close();
            $conn->close();
            if ($user['role'] === 'admin') {
                header("Location: admin.php");
            } else {
                header("Location: index.php");
            }
            exit();
        } else {

            $_SESSION['login_error'] = 'Incorrect email or password.';
            $_SESSION['active_form'] = 'login';
            $_SESSION['login_attempts'] = $loginAttempts + 1;
            if ($_SESSION['login_attempts'] >= 3) {
                $_SESSION['lockout_time'] = $currentTime;
            }
        }
    } else {

        $_SESSION['login_error'] = 'Incorrect email or password.';
        $_SESSION['active_form'] = 'login';
        $_SESSION['login_attempts'] = $loginAttempts + 1;
        if ($_SESSION['login_attempts'] >= 3) {
            $_SESSION['lockout_time'] = $currentTime;
        }
    }

    $stmt->close();
    $conn->close();
    header("Location: login&signup.php");
    exit();
}

 

here are some implementation practices - 

the form processing code and form should be on the same page. by putting them on separate pages, you are creating a lot of extra code. by only validating one input at a time and not having the form fields 'sticky', you are providing a poor User eXperience (UX).

by storing the 'login_attempts' and 'lockout_time' in session variables, a nefarious user/bot can get unlimited new login attempts by simply not propagating the session id cookie between requests. you must store this data persistently on the server in a database table.

the only user related value you should store in a session variable upon successful login is the user id (autoincrement primary index.) you should query on each page request to get any other user data, so that any changes made to the user data will take effect on the very next page request, without requiring the user to log out and back in again.

the way a 'remember me' operation should be implemented is that if the remember me checkbox is checked, at the point of successfully verifying the user's credentials, generate a unique token, store that in a cookie and in a database 'remember me' table that also includes the user id, and the current datatime, for a determining token expiration. on any page request, if the remember me token cookie is set, query to find a matching row in the remember me table. if there is a row and the token is not timed out, use the user id from that row to set the session variable that identifies who the logged in user is. the rest of the code then uses this value in the session variable, just like it was set in the login form processing code.

the registration process, unless being performed by an administrator, which your code is not doing, should not include the role. the role should not be something that the user can decide when they register.

modern php (8+) uses exceptions for database statement errors by default - connection, query, prepare, and execute. any discrete logic you currently have testing the result of these statements should be removed since it will never get executed upon an error.

both the username and email must be unique or you should only use the email and forget about a separate username. the correct way of determining if a unique value already exists in a database table is to define the column(s) as a unique index, just attempt to insert the data, and detect in the exception catch logic for the insert query if a duplicate index error (number) occurred.

any form processing code should keep for the form data as a set, in an array variable, then operate on elements in this array variable throughout the rest of the code. i.e. don't write out a line of code copying every $_POST variable to a discrete variable.

you need to trim ALL the user supplied inputs, mainly so that you can detect if all white-space characters were entered, before validating the data.

you need to use an array to hold user/validation errors, and validate all the inputs at once, storing the errors in the array using the field name as the array index. after the end of the validation logic, if there are no errors (the array will be empty), use the submitted form data.

in the login validation logic, all you really care about is that the required inputs are are not empty strings, after being trimmed. by providing additional feedback to a nefarious user/bot, you are helping narrow down the values they need to try.

  • Great Answer 1

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.