Skip to content

PHP Tutorial Authentication

Login Sessions

Lesson 285 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

After the password is verified, you start a logged-in session: you remember the user’s id in $_SESSION.

The login page

login.php
PHP
<?php
declare(strict_types=1);

session_set_cookie_params([
    "lifetime" => 0,
    "path" => "/",
    "secure" => !empty($_SERVER["HTTPS"]),
    "httponly" => true,
    "samesite" => "Lax",
]);
session_start();

require __DIR__ . "/functions.php";   // h(), csrfToken(), csrfCheck(), attemptLogin()

$pdo = require __DIR__ . "/db.php";

// Already logged in? Go to the dashboard.
if (isset($_SESSION["user_id"])) {
    header("Location: dashboard.php");
    exit;
}

$error = null;
$email = "";

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    csrfCheck();

    $email = trim((string) ($_POST["email"] ?? ""));
    $password = (string) ($_POST["password"] ?? "");

    $user = attemptLogin($pdo, $email, $password);

    if ($user === null) {
        $error = "Email or password is incorrect.";
    } else {
        session_regenerate_id(true);          // new session ID: prevents session fixation
        $_SESSION["user_id"] = (int) $user["id"];
        $_SESSION["logged_in_at"] = time();

        header("Location: dashboard.php");
        exit;
    }
}
?>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><title>Log in</title></head>
<body>
  <h1>Log in</h1>

  <?php if ($error): ?>
    <p role="alert"><?= h($error) ?></p>
  <?php endif; ?>

  <form method="post">
    <input type="hidden" name="csrf_token" value="<?= h(csrfToken()) ?>">

    <p>
      <label for="email">Email</label><br>
      <input type="email" id="email" name="email" value="<?= h($email) ?>" autocomplete="username" required>
    </p>
    <p>
      <label for="password">Password</label><br>
      <input type="password" id="password" name="password" autocomplete="current-password" required>
    </p>
    <button type="submit">Log in</button>
  </form>
</body>
</html>

The important lines

session_regenerate_id(true) Creates a new session ID at the moment of login and deletes the old session. If an attacker managed to give the victim a known session ID before login (session fixation), that ID becomes useless. See Session Fixation.

$_SESSION["user_id"] = ... Store the id only. Do not store the password, the hash, or the whole user row. Load details from the database when you need them.

One generic error message “Email or password is incorrect.” Never say which one was wrong.

csrfCheck() Protects the login form against CSRF (see CSRF).

autocomplete attributes username and current-password let password managers fill the form, which helps users choose strong, unique passwords.

Who is logged in?

On other pages, read the id and load the user when you need their details.

index.php
PHP
<?php
function currentUser(PDO $pdo, array $session): ?array
{
    $id = $session["user_id"] ?? null;

    if (!is_int($id)) {
        return null;
    }

    $statement = $pdo->prepare("SELECT id, name, email, role FROM users WHERE id = :id");
    $statement->execute(["id" => $id]);

    return $statement->fetch() ?: null;
}

$pdo = require __DIR__ . "/db.php";

$user = currentUser($pdo, ["user_id" => 2]);

echo $user["name"] . " (" . $user["role"] . ")\n";
var_dump(currentUser($pdo, []));
Output
OUTPUT
Ben (user)
NULL

Loading the user on each request means that if you deactivate or delete an account, that user is locked out immediately, even if their session is still alive.

Safe redirects after login

Sometimes you send the visitor back to the page they wanted. Never redirect to a URL taken from the request without checking it. See Post/Redirect/Get for an allow-list.

Timeouts

A user who forgets to log out should not stay logged in forever on a shared computer. Check the last activity time and end the session after a period of inactivity. Secure Session Practices shows how.

warning
Storing a "logged_in" flag in a cookie
A cookie can be edited. $_COOKIE["logged_in"] = "1" proves nothing. The login state belongs in the server-side session.