08-23-2024, 03:40 AM
PHP Login
Summary: in this tutorial, you’ll learn how to create a login form using a username and password.
Prerequisites
To start this tutorial, you need to complete the previous tutorial that creates a registration form.
Introduction to the PHP login form
In the previous tutorial, you learned how to create a form that allows users to register for accounts. Once the users register successfully, you redirect them to the login page so that they can use their usernames and passwords to log in.
The login page will contain a form that consists of the username and password inputs and a login button as follows:
PHP Login
To log in, users need to enter their username and password and click the login button.
If the username and password match, you can redirect them to a password-protected page. Otherwise, you redirect the users back to the login page with an error message:
Note that it’s more secure to issue a generic message (invalid username or password) when either username or password doesn’t match.
Create the login form
First, create the login.php page in the public folder.
Second, define a login form with the username and password inputs and a login button:
Like the register.php page, you can reuse the header.php and footer.php files from the src/inc folder and use the view() function to load them to the login.php page as follows:
The login form submits to login.php. Therefore, you can check if the HTTP request method is POST before processing the form.
To process the form, you create the login.php in the src folder.
How it works.
First, define two variables to store the sanitized data and error messages:
Second, check if the HTTP request method is POST using the is_post_request() function:
Third, sanitize and validate user inputs using the filter() function:
Fourth, if either username or password is not provided, redirect users to the login.php page using the post-redirect-get (PRG) technique and set the $errors and $inputs in the session using the redirect_with() function.
Fifth, call the login() function to verify the username and password.
If either username or password doesn’t match, set an error message with the key login and redirect users back to the login.php page:
Seventh, if both username and password match, redirect users to the index.php page:
The index.php is the password-protected page. It means that only logged-in users can access it—more on this in the next section.
Finally, get the $inputs and $errors from the session if the HTTP request method is GET.
Show the entered data & error message on the login.php page
The public/login.php needs to change to the following to show the user inputs as well as error messages:
Since the login() function doesn’t exist, we need to define it.
Define the login() function
The login() function accepts username and password and returns true if they are valid. The logic of the login() function is as follows:
First, find the username in the users table. If the user exists, go to step 2.
Second, verify if the password matches.
Third, if the username doesn’t exist or the password doesn’t match, issue an error message.
To find a user from the users table by username, you can define a function find_user_by_username() in the src/auth.php file:
If a username exists in the users table, the find_user_by_username() function returns an associative array with two elements whose keys are username and password. Otherwise, it returns false.
Since you store the password hash in the database, you need to use the built-in password_verify() function to match the plain password with a hash.
The password_verify() function returns true if a plain text password matches a hash, or false otherwise.
If both username and password match, you can log the user in. To do that, you need to set a value in the $_SESSION for example:
In subsequent requests, you can check the $_SESSION variable to see if the user with a username is currently logged in. For example:
Here’s the complete login() function:
Note that you should call the session_regenerate_id() function to regenerate a new session id if when users start logging in. It helps prevent the session fixation attack.
The following defines the is_user_logged_in() function in the src/auth.php file, which returns true if a user is currently logged in:
If users have not logged in, you can redirect them to the login.php page. The following defines the require_login() function that redirects to the login.php page if the current user is not logged in:
And you can call the require_login() function at the beginning of any page that requires login.
For example, you can call it in the public/index.php page like this:
Log a user out
To log a user out, you need to remove the value you set when logged in and redirect to the login.php page.
In the login() function, you add the username to the $_SESSION variable. Therefore, you need to remove it to log the user out.
The following defines the logout() function in the auth.php that logs a user out by removing the username and user_id from the $_SESSION variable:
The following defines the current_user() function that returns the username of the currently logged in user:
Create the logout link
When users log in successfully, they are redirected to the index.php.
On the index.php, you can show a welcome message as well as a logout link like this:
When users click the logout link, you need to call the logout() function to log the users out.
To do that, you need to create the logout.php in the public folder.
In the logout.php, you need to call the logout() function as follows:
Redirect if users already logged in
If users already logged in and navigate to the login.php or register.php page, you need to redirect them to the index.php.
To do it, you can add the following code to the beginning of the login.php and register.php file in the src folder:
The following shows the login.php in the src folder:
Summary
Use the password_verify() function to verify the password.
Call the session_regenerate_id() function to prevent the session fixation attack.
Add one or more values (username and user idl) to the $_SESSION variable to mark if a user has been logged in.
Summary: in this tutorial, you’ll learn how to create a login form using a username and password.
Prerequisites
To start this tutorial, you need to complete the previous tutorial that creates a registration form.
Introduction to the PHP login form
In the previous tutorial, you learned how to create a form that allows users to register for accounts. Once the users register successfully, you redirect them to the login page so that they can use their usernames and passwords to log in.
The login page will contain a form that consists of the username and password inputs and a login button as follows:
PHP Login
To log in, users need to enter their username and password and click the login button.
If the username and password match, you can redirect them to a password-protected page. Otherwise, you redirect the users back to the login page with an error message:
Note that it’s more secure to issue a generic message (invalid username or password) when either username or password doesn’t match.
Create the login form
First, create the login.php page in the public folder.
Second, define a login form with the username and password inputs and a login button:
PHP Code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://www.phptutorial.net/app/css/style.css">
<title>Login</title>
</head>
<body>
<main>
<form action="login.php" method="post">
<h1>Login</h1>
<div>
<label for="username">Username:</label>
<input type="text" name="username" id="username">
</div>
<div>
<label for="password">Password:</label>
<input type="password" name="password" id="password">
</div>
<section>
<button type="submit">Login</button>
<a href="register.php">Register</a>
</section>
</form>
</main>
</body>
</html>
Like the register.php page, you can reuse the header.php and footer.php files from the src/inc folder and use the view() function to load them to the login.php page as follows:
PHP Code:
<?php
require __DIR__ . '/../src/bootstrap.php';
?>
<?php view('header', ['title' => 'Login']) ?>
<main>
<form action="login.php" method="post">
<h1>Login</h1>
<div>
<label for="username">Username:</label>
<input type="text" name="username" id="username">
</div>
<div>
<label for="password">Password:</label>
<input type="password" name="password" id="password">
</div>
<section>
<button type="submit">Login</button>
<a href="register.php">Register</a>
</section>
</form>
</main>
<?php view('footer') ?>
The login form submits to login.php. Therefore, you can check if the HTTP request method is POST before processing the form.
To process the form, you create the login.php in the src folder.
PHP Code:
<?php
$inputs = [];
$errors = [];
if (is_post_request()) {
[$inputs, $errors] = filter($_POST, [
'username' => 'string | required',
'password' => 'string | required'
]);
if ($errors) {
redirect_with('login.php', ['errors' => $errors, 'inputs' => $inputs]);
}
// if login fails
if (!login($inputs['username'], $inputs['password'])) {
$errors['login'] = 'Invalid username or password';
redirect_with('login.php', [
'errors' => $errors,
'inputs' => $inputs
]);
}
// login successfully
redirect_to('index.php');
} else if (is_get_request()) {
[$errors, $inputs] = session_flash('errors', 'inputs');
}
How it works.
First, define two variables to store the sanitized data and error messages:
PHP Code:
$inputs = [];
$errors = [];
Second, check if the HTTP request method is POST using the is_post_request() function:
PHP Code:
if (is_post_request()) {
// ...
}
Third, sanitize and validate user inputs using the filter() function:
PHP Code:
[$inputs, $errors] = filter($_POST, [
'username' => 'string | required',
'password' => 'string | required'
]);
Fourth, if either username or password is not provided, redirect users to the login.php page using the post-redirect-get (PRG) technique and set the $errors and $inputs in the session using the redirect_with() function.
PHP Code:
if ($errors) {
redirect_with('login.php', [
'errors' => $errors,
'inputs' => $inputs
]);
}
Fifth, call the login() function to verify the username and password.
If either username or password doesn’t match, set an error message with the key login and redirect users back to the login.php page:
PHP Code:
<?php
if (!login($inputs['username'], $inputs['password'])) {
$errors['login'] = 'Invalid username or password';
redirect_with('login.php', [
'errors' => $errors,
'inputs' => $inputs
]);
}
Seventh, if both username and password match, redirect users to the index.php page:
PHP Code:
redirect_to('index.php');
The index.php is the password-protected page. It means that only logged-in users can access it—more on this in the next section.
Finally, get the $inputs and $errors from the session if the HTTP request method is GET.
PHP Code:
[$errors, $inputs] = session_flash('errors', 'inputs');
Show the entered data & error message on the login.php page
The public/login.php needs to change to the following to show the user inputs as well as error messages:
PHP Code:
<?php
require __DIR__ . '/../src/bootstrap.php';
require __DIR__ . '/../src/login.php';
?>
<?php view('header', ['title' => 'Login']) ?>
<?php if (isset($errors['login'])) : ?>
<div class="alert alert-error">
<?= $errors['login'] ?>
</div>
<?php endif ?>
<form action="login.php" method="post">
<h1>Login</h1>
<div>
<label for="username">Username:</label>
<input type="text" name="username" id="username" value="<?= $inputs['username'] ?? '' ?>">
<small><?= $errors['username'] ?? '' ?></small>
</div>
<div>
<label for="password">Password:</label>
<input type="password" name="password" id="password">
<small><?= $errors['password'] ?? '' ?></small>
</div>
<section>
<button type="submit">Login</button>
<a href="register.php">Register</a>
</section>
</form>
<?php view('footer') ?>
Since the login() function doesn’t exist, we need to define it.
Define the login() function
The login() function accepts username and password and returns true if they are valid. The logic of the login() function is as follows:
First, find the username in the users table. If the user exists, go to step 2.
Second, verify if the password matches.
Third, if the username doesn’t exist or the password doesn’t match, issue an error message.
To find a user from the users table by username, you can define a function find_user_by_username() in the src/auth.php file:
PHP Code:
function find_user_by_username(string $username)
{
$sql = 'SELECT username, password
FROM users
WHERE username=:username';
$statement = db()->prepare($sql);
$statement->bindValue(':username', $username, PDO::PARAM_STR);
$statement->execute();
return $statement->fetch(PDO::FETCH_ASSOC);
}
If a username exists in the users table, the find_user_by_username() function returns an associative array with two elements whose keys are username and password. Otherwise, it returns false.
Since you store the password hash in the database, you need to use the built-in password_verify() function to match the plain password with a hash.
The password_verify() function returns true if a plain text password matches a hash, or false otherwise.
If both username and password match, you can log the user in. To do that, you need to set a value in the $_SESSION for example:
PHP Code:
$_SESSION['username'] = $username;
In subsequent requests, you can check the $_SESSION variable to see if the user with a username is currently logged in. For example:
PHP Code:
// check if the username has been logged in
if(isset($_SESSION['username'])) {
// already logged in
// ...
}
Here’s the complete login() function:
PHP Code:
function login(string $username, string $password): bool
{
$user = find_user_by_username($username);
// if user found, check the password
if ($user && password_verify($password, $user['password'])) {
// prevent session fixation attack
session_regenerate_id();
// set username in the session
$_SESSION['username'] = $user['username'];
$_SESSION['user_id'] = $user['id'];
return true;
}
return false;
}
Note that you should call the session_regenerate_id() function to regenerate a new session id if when users start logging in. It helps prevent the session fixation attack.
The following defines the is_user_logged_in() function in the src/auth.php file, which returns true if a user is currently logged in:
PHP Code:
function is_user_logged_in(): bool
{
return isset($_SESSION['username']);
}
If users have not logged in, you can redirect them to the login.php page. The following defines the require_login() function that redirects to the login.php page if the current user is not logged in:
PHP Code:
function require_login(): void
{
if (!is_user_logged_in()) {
redirect_to('login.php');
}
}
And you can call the require_login() function at the beginning of any page that requires login.
For example, you can call it in the public/index.php page like this:
PHP Code:
<?php
require __DIR__ . '/../src/bootstrap.php';
require_login();
?>
Log a user out
To log a user out, you need to remove the value you set when logged in and redirect to the login.php page.
In the login() function, you add the username to the $_SESSION variable. Therefore, you need to remove it to log the user out.
The following defines the logout() function in the auth.php that logs a user out by removing the username and user_id from the $_SESSION variable:
PHP Code:
function logout(): void
{
if (is_user_logged_in()) {
unset($_SESSION['username'], $_SESSION['user_id']);
session_destroy();
redirect_to('login.php');
}
}
The following defines the current_user() function that returns the username of the currently logged in user:
PHP Code:
function current_user()
{
if (is_user_logged_in()) {
return $_SESSION['username'];
}
return null;
}
Create the logout link
When users log in successfully, they are redirected to the index.php.
On the index.php, you can show a welcome message as well as a logout link like this:
PHP Code:
<?php
require __DIR__ . '/../src/bootstrap.php';
require_login();
?>
<?php view('header', ['title' => 'Dashboard']) ?>
<p>Welcome <?= current_user() ?> <a href="logout.php">Logout</a></p>
<?php view('footer') ?>
When users click the logout link, you need to call the logout() function to log the users out.
To do that, you need to create the logout.php in the public folder.
In the logout.php, you need to call the logout() function as follows:
PHP Code:
<?php
require __DIR__ . '/../src/bootstrap.php';
logout();
Redirect if users already logged in
If users already logged in and navigate to the login.php or register.php page, you need to redirect them to the index.php.
To do it, you can add the following code to the beginning of the login.php and register.php file in the src folder:
PHP Code:
<?php
if (is_user_logged_in()) {
redirect_to('index.php');
}
The following shows the login.php in the src folder:
PHP Code:
<?php
if (is_user_logged_in()) {
redirect_to('index.php');
}
$inputs = [];
$errors = [];
if (is_post_request()) {
// sanitize & validate user inputs
[$inputs, $errors] = filter($_POST, [
'username' => 'string | required',
'password' => 'string | required'
]);
// if validation error
if ($errors) {
redirect_with('login.php', [
'errors' => $errors,
'inputs' => $inputs
]);
}
// if login fails
if (!login($inputs['username'], $inputs['password'])) {
$errors['login'] = 'Invalid username or password';
redirect_with('login.php', [
'errors' => $errors,
'inputs' => $inputs
]);
}
// login successfully
redirect_to('index.php');
} else if (is_get_request()) {
[$errors, $inputs] = session_flash('errors', 'inputs');
}
Summary
Use the password_verify() function to verify the password.
Call the session_regenerate_id() function to prevent the session fixation attack.
Add one or more values (username and user idl) to the $_SESSION variable to mark if a user has been logged in.