# Creating a Login Form

This standalone login page validates credentials with PDO, starts a secure session and redirects authenticated users. It uses one generic failure message so it does not reveal whether a username exists.

## Login page

~~~php
<?php
session_start();
require __DIR__ . "/includes/db.php";

$error = "";

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $username = trim($_POST["username"] ?? "");
    $password = $_POST["password"] ?? "";

    $stmt = $pdo->prepare(
        "SELECT user_id, username, password, role
         FROM users
         WHERE username = :username"
    );
    $stmt->execute(["username" => $username]);
    $user = $stmt->fetch();

    if ($user && password_verify($password, $user["password"])) {
        session_regenerate_id(true);
        $_SESSION["user_id"] = (int) $user["user_id"];
        $_SESSION["username"] = $user["username"];
        $_SESSION["role"] = $user["role"];

        header("Location: dashboard.php");
        exit;
    }

    $error = "Username or password was not accepted.";
}
?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Log in</title>
</head>
<body>
<h1>Log in</h1>

<?php if ($error): ?>
  <p role="alert"><?= htmlspecialchars($error) ?></p>
<?php endif; ?>

<form method="post">
  <label for="username">Username</label>
  <input id="username" name="username" autocomplete="username" required>

  <label for="password">Password</label>
  <input id="password" name="password" type="password"
         autocomplete="current-password" required>

  <button type="submit">Log in</button>
</form>
</body>
</html>
~~~

## Why the security steps matter

- prepared statements keep input separate from SQL
- `password_verify()` checks the stored hash
- session ID regeneration reduces session fixation risk
- role comes from the database, not the form
- a generic error reduces account discovery

## Test

Test a valid standard user, a valid administrator, a wrong password, an unknown username and empty fields. Confirm that failed attempts do not create session identity values.

## Check

- [ ] Session starts before output.
- [ ] Query uses a prepared statement.
- [ ] Password hash is verified correctly.
- [ ] Session ID changes after login.
- [ ] User ID, username and role are stored.
- [ ] Failure message is generic.