I want to build a login system in PHP. There are 5 files:
db.phpcontains database utilities.user.phpfor login and logout functions.login.phpis login page.index.phpis the main page.logout.phpis a page that logs users out and redirects tologin.php.
The login part seems to work as expected (by checking the database, omitted here), but the redirection from login.php to index.php doesn't seem to work. The same login page appears again.
But when I remove this part in index.php:
// Must be logged in first
if (!isset($_SESSION['username'])) {
gotoPage('login.php');
}
it redirects successfully, and then the logout fails.
Also, if a user logs out and clicks the undo button in the browser, it takes him to the main page (index.php) which he is not supposed to see after logging out.
I don't know what exactly is preventing the redirection and the logout. Any help (or advice) is appreciated.
Note: I've looked at similar questions on SO, none of the answers provided solved the issue.
db.php content:
<?php
function getDBInstance() : PDO
{
static $dbInstance;
if (!$dbInstance) {
return new PDO(
'mysql:host=localhost;dbname=DummyUserAccounts;charset=UTF8',
'dummyuser',
'...',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
}
return $dbInstance;
}
function register_user(string $firstname, string $lastname, string $username, string $password): bool
{
// PROC_USER_ADD inserts one user into the User table.
$query = 'CALL PROC_USER_ADD(:firstname, :lastname, :username, :password)';
$statement = getDBInstance()->prepare($query);
$statement->bindValue(':firstname', $firstname, PDO::PARAM_STR);
$statement->bindValue(':lastname', $lastname, PDO::PARAM_STR);
$statement->bindValue(':username', $username, PDO::PARAM_STR);
$statement->bindValue(':password', password_hash($password, PASSWORD_BCRYPT), PDO::PARAM_STR);
return $statement->execute();
}
?>
user.php content:
<?php
require 'db.php';
function sanitize($field)
{
$field = trim($field);
$field = stripslashes($field);
$field = htmlspecialchars($field);
return $field;
}
function gotoPage(string $page) : void
{
header('Location: ' . $page);
exit;
}
function loginUser(string $username, string $password) : bool
{
// Search for the user in the database
$queryString = 'SELECT username, password FROM user WHERE username = :username';
$statement = getDBInstance()->prepare($queryString);
$statement->bindValue(':username', $username, PDO::PARAM_STR);
$statement->execute();
$user = $statement->fetch(PDO::FETCH_ASSOC);
// Successful login?
if ($user && password_verify($password, $user['password'])) {
// Create a new Session ID
session_start();
// Write session data
$_SESSION['username'] = $username;
return true;
}
return false;
}
function logoutCurrentUser() : void
{
if (isset($_SESSION['username'])) {
unset($_SESSION['username']);
session_destroy();
gotoPage('login.php');
}
}
?>
login.php content:
<?php
require 'user.php';
// Can't login twice.
if (isset($_SESSION['username'])) {
gotoPage('index.php');
}
$errorMessage = "";
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// Check for empty fields
if (empty($_POST['username']) || empty($_POST['password'])) {
$errorMessage = "Both Username and Password are required!";
} else {
// Sanitize fields
$username = sanitize($_POST['username']);
$password = sanitize($_POST['password']);
// Login user
if (!loginUser($username, $password)) {
$errorMessage = "Invalid username and/or password.";
} else {
gotoPage('index.php');
}
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<form action="login.php" method="post">
<div>
<label>Username</label>
<input type="text" name="username">
</div>
<div>
<label>Password</label>
<input type="password" name="password">
</div>
<div>
<button type="submit">Login</button>
</div>
<div>
<span style="color:red"><?php echo $errorMessage ?></span>
</div>
</form>
</body>
</html>
index.php content:
<?php
require 'user.php';
// Must be logged in first
if (!isset($_SESSION['username'])) {
gotoPage('login.php');
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Main Page</title>
</head>
<body>
<nav>
<a href="logout.php">Logout</a>
</nav>
<!--Main page goes here-->
</body>
</html>
logout.php content:
<?php
require 'user.php';
logoutCurrentUser();
gotoPage('login.php');
?>