Secure Registration and Login System with PHP and MySQL

The login system enables users to create and log in to their accounts, allowing them to access website content. The login system is a key feature for every membership website. If you want to restrict access to the website content and allow only the logged-in user to access the content, user login functionality needs to be implemented. User registration and login system can be integrated easily with PHP and MySQL. In this tutorial, we’ll show you how to build a secure login system with PHP and MySQL.

In this PHP Login System with MySQL (user authentication) script, we will implement the following features:

  • User Registration (first_name, last_name, email, password)
  • User Login (email, password)
  • User Logout
  • Forgot Password Recovery (email -> token emailed -> reset)
  • Account page showing logged-in user details
  • Session Management
  • Password Hashing
  • Input Validation
  • Uses prepared statements (mysqli) to avoid SQL injection.

Before getting started to build User Login System with PHP, take a look at the complete file structure of the project.

login_system_with_php/
├── config.php
├── index.php
├── register.php
├── forgot.php
├── reset.php
├── account.php
├── logout.php
└── assets/
    └── style.css

Create Database Table

Create a MySQL database (ex: codexworld_db) and a table named users using the following SQL query. The users table includes fields for user details such as first name, last name, email, password, and created date.

CREATE TABLE `users` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `first_name` VARCHAR(50) NOT NULL,
  `last_name` VARCHAR(50) NOT NULL,
  `email` VARCHAR(100) NOT NULL UNIQUE,
  `password` VARCHAR(255) NOT NULL,
  `reset_token` VARCHAR(255) DEFAULT NULL,
  `reset_expires` DATETIME DEFAULT NULL,
  `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Database Configuration (config.php)

This file is used to set database connection parameters. Update the database configuration according to your MySQL setup. Also, set the base URL and mail settings for password recovery.

  • In the DB_HOST, DB_USER, DB_PASS, and DB_NAME constants, set the database connection parameters according to your MySQL setup.
  • In the BASE_URL constant, set the base URL according to your project path.
  • In the MAIL_FROM constant, set the email address from which password reset emails will be sent.

There are also helper functions to connect to the database and sanitize user input.

<?php 

// Database configuration (adjust according to your mySQL setup)
define('DB_HOST''localhost');
define('DB_USER''root');
define('DB_PASS''root_pass');
define('DB_NAME''codexworld_db');

// Base URL (adjust if necessary)
define('BASE_URL''http://localhost/secure_login_system_with_php/');

// Mail settings: when using localhost, ensure a mailer is configured or use SMTP in production
define('MAIL_FROM''no-reply@example.com');

// Start session
if (session_status() === PHP_SESSION_NONE) {
    
session_start();
}

// Create mysqli connection
function db_connect() {
    
$mysqli = new mysqli(DB_HOSTDB_USERDB_PASSDB_NAME);
    if (
$mysqli->connect_errno) {
        die(
'Database connection failed: ' $mysqli->connect_error);
    }
    
// set charset
    
$mysqli->set_charset('utf8mb4');
    return 
$mysqli;
}

// Helper: sanitize input
function e($str) {
    return 
htmlspecialchars($strENT_QUOTES'UTF-8');
}

?>

Registration Page (register.php)

This file contains the user registration form and handles the registration process. It includes input validation, password hashing, and storing user details in the database. It also checks for duplicate email addresses to prevent multiple registrations with the same email.

  • Includes configuration file.
  • If the user is already logged in (i.e., a session exists), they are redirected to the account page.
  • Initializes error and old input arrays to store validation errors and previously entered values.
  • The form collects first name, last name, email, password, and password confirmation.
  • Handles form submission:
    • Retrieves and trims form input values.
    • Stores old input values to repopulate the form in case of errors.
    • Validates input fields and populates the errors array if any validation fails.
    • Input validation checks for required fields, valid email format, password length, and matching passwords.
    • If validation passes, the password is hashed using password_hash() and the user details are inserted into the database using prepared statements to prevent SQL injection.
  • Upon successful registration, the user is redirected to the login page with a success message.
<?php 
// Include configuration file
require_once 'config.php';

// If already logged in, redirect to account
if (!empty($_SESSION['user_id'])) {
    
header('Location: account.php');
    exit;
}

// Initialize
$errors = [];
$old = ['first_name'=>'','last_name'=>'','email'=>''];

// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit'])) {
    
$first trim($_POST['first_name'] ?? '');
    
$last trim($_POST['last_name'] ?? '');
    
$email trim($_POST['email'] ?? '');
    
$password $_POST['password'] ?? '';
    
$confirm $_POST['password_confirm'] ?? '';
    
$old = ['first_name'=>$first,'last_name'=>$last,'email'=>$email];

    
// Validation
    
if ($first === '') {
        
$errors['first_name'] = 'First name is required';
    }
    if (
$last === '') {
        
$errors['last_name'] = 'Last name is required';
    }
    if (
$email === '') {
        
$errors['email'] = 'Email is required';
    } elseif (!
filter_var($emailFILTER_VALIDATE_EMAIL)) {
        
$errors['email'] = 'Email is not valid';
    }
    if (
$password === '') {
        
$errors['password'] = 'Password is required';
    } elseif (
strlen($password) < 8) {
        
$errors['password'] = 'Password must be at least 8 characters';
    }
    if (
$confirm !== $password) {
        
$errors['password_confirm'] = 'Passwords do not match';
    }

    if (empty(
$errors)) {
        
$db db_connect();
        
$stmt $db->prepare('SELECT id FROM users WHERE email = ? LIMIT 1');
        
$stmt->bind_param('s'$email);
        
$stmt->execute();
        
$stmt->store_result();
        if (
$stmt->num_rows 0) {
            
$errors['email'] = 'Email is already registered';
        } else {
            
$hash password_hash($passwordPASSWORD_DEFAULT);
            
$ins $db->prepare('INSERT INTO users (first_name,last_name,email,password) VALUES (?,?,?,?)');
            
$ins->bind_param('ssss'$first$last$email$hash);
            if (
$ins->execute()) {
                
// set a flash message and redirect to login (index.php)
                
$_SESSION['flash_success'] = 'Registration successful. You may now log in.';
                
header('Location: '.BASE_URL);
                exit;
            } else {
                
$errors['general'] = 'Registration failed. Please try again.';
            }
        }
        
$stmt->close();
        
$db->close();
    }
}
?> <div class="card"> <h2>Registration</h2> <p class="muted">Please fill this form to create an account.</p> <?php if (!empty($errors)): ?> <div class="top-messages error"> <?php foreach ($errors as $err): ?> <div><?php echo e($err); ?></div> <?php endforeach; ?> </div> <?php elseif (!empty($success)): ?> <div class="top-messages success"><?php echo e($success); ?></div> <?php endif; ?> <form method="post"> <div class="form-group"> <label for="first_name">First name</label> <input type="text" id="first_name" name="first_name" value="<?php echo e($old['first_name']); ?>" required> </div> <div class="form-group"> <label for="last_name">Last name</label> <input type="text" id="last_name" name="last_name" value="<?php echo e($old['last_name']); ?>" required> </div> <div class="form-group"> <label for="email_r">Email</label> <input type="email" id="email_r" name="email" value="<?php echo e($old['email']); ?>" required> </div> <div class="form-group"> <label for="password">Password</label> <input type="password" id="password" name="password" required> </div> <div class="form-group"> <label for="password_confirm">Confirm Password</label> <input type="password" id="password_confirm" name="password_confirm" required> </div> <div> <input type="submit" name="submit" class="btn" value="Register"> <a class="btn secondary" href="<?php echo BASE_URL?>">Back to login</a> </div> <div class="switch-links"> <span class="muted">Already registered?</span> <a href="<?php echo BASE_URL?>">Login</a> </div> </form> </div>

Login Form (index.php)

This is the login form that users will see when they visit the root URL of your application. It includes fields for email and password, as well as links to the registration page. The form submits to the same page (index.php) using the POST method.

The PHP code at the top of the file handles the following:

  • If the user is already logged in (i.e., a session variable for user_id is set), they are redirected to their account page.
  • On form submission, the email and password are retrieved from the POST data, and validate these credentials against the database. Password verification is done using password_verify().
  • If the credentials are valid, the user_id is stored in the session, and the user is redirected to their account page.
  • If the credentials are invalid, an error message is added to the errors array to be displayed.
  • If there are any error messages (e.g., from a failed login attempt), they are displayed at the top of the form.
  • If there is a success message (e.g., from a successful registration), it is also displayed at the top of the form.
<?php 
// Include configuration file
require_once 'config.php';

// If already logged in, redirect to account
if (!empty($_SESSION['user_id'])) {
    
header('Location: account.php');
    exit;
}

// Initialize
$errors = [];
$old = ['email'=>''];

// show flash message from registration if set
$flash_success '';
if (!empty(
$_SESSION['flash_success'])) {
    
$flash_success $_SESSION['flash_success'];
    unset(
$_SESSION['flash_success']);
}

// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit'])) {
    
$email trim($_POST['email'] ?? '');
    
$password $_POST['password'] ?? '';
    
$old['email'] = $email;

    if (
$email === '') {
        
$errors['email'] = 'Email is required';
    } elseif (!
filter_var($emailFILTER_VALIDATE_EMAIL)) {
        
$errors['email'] = 'Email is not valid';
    }
    if (
$password === '') {
        
$errors['password'] = 'Password is required';
    }

    if (empty(
$errors)) {
        
$db db_connect();
        
$stmt $db->prepare('SELECT id, first_name, last_name, email, password FROM users WHERE email = ? LIMIT 1');
        
$stmt->bind_param('s'$email);
        
$stmt->execute();
        
$res $stmt->get_result();
        if (
$user $res->fetch_assoc()) {
            if (
password_verify($password$user['password'])) {
                
// login success
                
session_regenerate_id(true);
                
$_SESSION['user_id'] = $user['id'];
                
header('Location: account.php');
                exit;
            } else {
                
$errors['general'] = 'Email or password is incorrect';
            }
        } else {
            
$errors['general'] = 'Email or password is incorrect';
        }
        
$stmt->close();
        
$db->close();
    }
}
?> <div class="card"> <h2>Login</h2> <p class="muted">Use the login form below to access your account.</p> <?php if (!empty($errors)): ?> <div class="top-messages error"> <?php foreach ($errors as $err): ?> <div><?php echo e($err); ?></div> <?php endforeach; ?> </div> <?php elseif (!empty($flash_success)): ?> <div class="top-messages success"><?php echo e($flash_success); ?></div> <?php endif; ?> <form method="post"> <div class="form-group"> <label for="email">Email</label> <input id="email" name="email" type="email" value="<?php echo e($old['email']); ?>" required> </div> <div class="form-group"> <label for="password_l">Password</label> <input id="password_l" name="password" type="password" required> <span class="muted"><a href="forgot.php">Forgot Password?</a></span> </div> <div> <input type="submit" name="submit" class="btn" value="Login"> </div> <div class="switch-links"> <span class="muted">Don't have an account?</span> <a href="register.php">Register</a> </div> </form> </div>

Account Page (account.php)

This page is accessible only to logged-in users and displays their account information. It includes:

  • Check if the user is logged in by verifying the session.
  • If not logged in, redirect to the login page.
  • Fetch user details from the database using the user ID stored in the session.
  • Display user information such as first name, last name, email, and account creation date.
  • Provide a logout option that destroys the session and redirects to the login page.
<?php 
// Include configuration file
require_once 'config.php';

// If not logged in, redirect to login
if (empty($_SESSION['user_id'])) {
    
header('Location: ' BASE_URL);
    exit;
}

// Fetch user details
$db db_connect();
$stmt $db->prepare('SELECT id, first_name, last_name, email, created_at FROM users WHERE id = ? LIMIT 1');
$stmt->bind_param('i'$_SESSION['user_id']);
$stmt->execute();
$res $stmt->get_result();
if (!
$user $res->fetch_assoc()) {
    
// user missing
    
session_destroy();
    
header('Location: ' BASE_URL);
    exit;
}
$stmt->close();
$db->close();

?> <div class="card"> <div class="account"> <h2>My Account</h2> <div> <a class="btn secondary" href="logout.php">Logout</a> </div> </div> <div style="margin-top:12px"> <p><strong>First name:</strong> <?php echo e($user['first_name']); ?></p> <p><strong>Last name:</strong> <?php echo e($user['last_name']); ?></p> <p><strong>Email:</strong> <?php echo e($user['email']); ?></p> <p class="muted">Member since: <?php echo e($user['created_at']); ?></p> </div> </div>

Logout Page (logout.php)

This page logs the user out by destroying the session and redirects them to the login page.

  • Include the configuration file to access session settings.
  • Clear the session data and destroy the session.
  • Redirect to the login page.
<?php 
// Include configuration file
require_once 'config.php';

// clear session
$_SESSION = [];
if (
ini_get('session.use_cookies')) {
    
$params session_get_cookie_params();
    
setcookie(session_name(), ''time() - 42000,
        
$params['path'], $params['domain'], $params['secure'], $params['httponly']
    );
}
session_destroy();

// redirect to homepage
header('Location: ' BASE_URL);
exit;
?>

Forgot Password Page (forgot.php)

This page allows users to request a password reset link by entering their email address. It includes:

  • Form with email input field and submit button.
  • Validation to ensure the email is provided and in a valid format.
  • Database check to verify if the email exists.
  • If the email exists, generate a unique token, store it in the database with an expiration time, and send a reset link to the user’s email. PHP mail() function can be used for sending emails.
  • Display success or error messages based on the outcome.
<?php 
// Include configuration file
require_once 'config.php';

// Initialize
$errors = [];
$status '';

// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit'])) {
    
$email trim($_POST['email'] ?? '');
    if (
$email === '') {
        
$errors['email'] = 'Email is required';
    } elseif (!
filter_var($emailFILTER_VALIDATE_EMAIL)) {
        
$errors['email'] = 'Email is not valid';
    }

    if (empty(
$errors)) {
        
$db db_connect();
        
$stmt $db->prepare('SELECT id, first_name FROM users WHERE email = ? LIMIT 1');
        
$stmt->bind_param('s'$email);
        
$stmt->execute();
        
$res $stmt->get_result();
        if (
$user $res->fetch_assoc()) {
            
$token bin2hex(random_bytes(16));
            
$expires date('Y-m-d H:i:s'time() + 3600); // 1 hour
            
$up $db->prepare('UPDATE users SET reset_token = ?, reset_expires = ? WHERE id = ?');
            
$up->bind_param('ssi'$token$expires$user['id']);
            if (
$up->execute()) {
                
$resetLink BASE_URL 'reset.php?token=' $token;
                
$subject 'Password reset request';
                
$message "Hello " $user['first_name'] . ",\n\n" .
                    
"We received a request to reset your password. Click the link below to reset it:\n\n" .
                    
$resetLink "\n\nIf you didn't request this, please ignore this email.\n\n" .
                    
"Regards\n" MAIL_FROM;
                
$headers 'From: ' MAIL_FROM "\r\n" 'Reply-To: ' MAIL_FROM "\r\n";
                
// Try to send mail; on many dev environments this may not be configured.
                
@mail($email$subject$message$headers);

                
$status 'A password reset link has been sent to your email address. Please check your inbox.';
            } else {
                
$errors['general'] = 'Unable to create reset request. Try again later.';
            }
            
$up->close();
        } else {
            
// Don't reveal whether email exists
            
$status 'If the email exists in our system, a reset link has been sent.';
        }
        
$stmt->close();
        
$db->close();
    }
}
?> <div class="card"> <h2>Forgot Password</h2> <p class="muted">Enter your email address below to receive a password reset link.</p> <?php if (!empty($errors)): ?> <div class="top-messages error"> <?php foreach ($errors as $err): ?> <div><?php echo e($err); ?></div> <?php endforeach; ?> </div> <?php elseif ($status): ?> <div class="top-messages success"><?php echo e($status); ?></div> <?php endif; ?> <form method="post"> <div class="form-group"> <label for="email_f">Email</label> <input type="email" id="email_f" name="email" required> </div> <div> <input type="submit" name="submit" class="btn" value="Send Reset Link"> <a class="btn secondary" href="<?php echo BASE_URL?>">Back</a> </div> </form> </div>

Reset Password Page (reset.php)

This page allows users to reset their password using the token sent to their email. It includes:

  • Retrieves the token from the URL.
  • Handles form submission:
    • Validates the new password and confirmation match.
    • Checks if the token is valid and not expired.
    • Updates the user’s password in the database.
    • Deletes the used token from the database.
    • Sets a success message upon successful password reset.
  • Displays any errors or status messages.
  • Provides a form for entering a new password and confirming it.
<?php 
// Include configuration file
require_once 'config.php';

// Initialize
$errors = [];
$status '';

// Get token from URL
$token $_GET['token'] ?? '';

// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit'])) {
    
$token $_POST['token'] ?? '';
    
$password $_POST['password'] ?? '';
    
$confirm $_POST['password_confirm'] ?? '';

    if (
$password === '') {
        
$errors['password'] = 'Password is required';
    } elseif (
strlen($password) < 8) {
        
$errors['password'] = 'Password must be at least 8 characters';
    }
    if (
$confirm !== $password) {
        
$errors['password_confirm'] = 'Passwords do not match';
    }

    if (empty(
$errors)) {
        
$db db_connect();
        
$stmt $db->prepare('SELECT id, reset_expires FROM users WHERE reset_token = ? LIMIT 1');
        
$stmt->bind_param('s'$token);
        
$stmt->execute();
        
$res $stmt->get_result();
        if (
$user $res->fetch_assoc()) {
            if (
$user['reset_expires'] === null || strtotime($user['reset_expires']) < time()) {
                
$errors['general'] = 'Reset token has expired';
            } else {
                
$hash password_hash($passwordPASSWORD_DEFAULT);
                
$up $db->prepare('UPDATE users SET password = ?, reset_token = NULL, reset_expires = NULL WHERE id = ?');
                
$up->bind_param('si'$hash$user['id']);
                if (
$up->execute()) {
                    
$status 'Your password has been reset successfully. You may now log in.';
                } else {
                    
$errors['general'] = 'Unable to reset password. Try again later.';
                }
                
$up->close();
            }
        } else {
            
$errors['general'] = 'Invalid reset token';
        }
        
$stmt->close();
        
$db->close();
    }
}
?> <div class="card"> <h2>Reset Password</h2> <p class="muted">Reset your password using the form below.</p> <?php if (!empty($errors)): ?> <div class="top-messages error"> <?php foreach ($errors as $err): ?> <div><?php echo e($err); ?></div> <?php endforeach; ?> </div> <?php elseif ($status): ?> <div class="top-messages success"><?php echo e($status); ?></div> <div style="margin-top:12px"> <a class="btn" href="<?php echo BASE_URL?>">Go to Login</a> </div> <?php endif; ?> <?php if (empty($status)): ?> <form method="post"> <div class="form-group"> <label for="password">New Password</label> <input type="password" id="password" name="password" required> </div> <div class="form-group"> <label for="password_confirm">Confirm New Password</label> <input type="password" id="password_confirm" name="password_confirm" required> </div> <div> <input type="hidden" name="token" value="<?php echo e($token); ?>"> <input type="submit" name="submit" class="btn" value="Reset Password"> <a class="btn secondary" href="<?php echo BASE_URL?>">Cancel</a> </div> </form> <?php endif; ?> </div>

Conclusion

In this tutorial, we have created a simple user registration and login system using PHP and MySQL. We covered the following topics:

  • Setting up the database and creating the users table.
  • Creating a registration form (sign-up) and handling user input.
  • Implementing password hashing for security.
  • Creating a login form (sign-in) and validating user credentials.
  • Using PHP sessions to manage user authentication.
  • Implementing a logout functionality.

By following these steps, you can build a basic user authentication system for your PHP web applications. Remember to always prioritize security by using prepared statements to prevent SQL injection and hashing passwords before storing them in the database.

To make this code functional, ensure you have a proper PHP environment set up with access to a MySQL database. You can further enhance this system by adding features like adding CSRF protection tokens to forms, using a proper mailer (PHPMailer) with SMTP credentials, email verification, forgot password with email OTP verification functionality, password reset, and user profile management.

You can purchase a complete and ready-to-use PHP User Login System Script from the Premium Scripts section or download the standard version from the Download Source Code button below.

Looking for expert assistance to implement or extend this script’s functionality? Submit a Service Request

28 Comments

  1. Eli Said...
  2. Tanjib Rubyat Said...
  3. Nathaniel Said...
  4. Vasman Said...
  5. Syed Ali Ahamad Said...
  6. Edwin Said...
  7. Ananth Said...
  8. Kilo Said...
  9. Quadryl Said...
    • CodexWorld Said...
  10. Narayana Said...
  11. Adam Said...
  12. Suhe Said...
  13. Abbi Said...
  14. Ryan Said...
  15. Rakim Said...
    • CodexWorld Said...
  16. Deepjyoti Baiahya Said...
  17. Kalai Said...
  18. Lawrence Igwegbe Said...
  19. Punith Said...
  20. Olli Said...
  21. Ion Vladescu Said...
  22. Shannu Said...
  23. Robin Tyagi Said...
  24. Tom Said...

Leave a reply

construction Need this implemented in your project? Request Implementation Help → keyboard_double_arrow_up