PHP User Sessions Setup

Sorry if this is a duplicate question, but I am trying to make a login form and I cannot seem to add user session capability. What do I need to do to add this in? My code is already using session variables, but I do not know what else to do to use the session that’s being created.

This is my login authentication:

<?php

session_start();

// Change this to your connection info.
$DATABASE_HOST = 'localhost';
$DATABASE_USER = 'root';
$DATABASE_PASS = '';
$DATABASE_NAME = 'ccrp_db';
// Try and connect using the info above.
$con = mysqli_connect($DATABASE_HOST, $DATABASE_USER, $DATABASE_PASS, $DATABASE_NAME);
if ( mysqli_connect_errno() ) {
	// If there is an error with the connection, stop the script and display the error.
	die ('Failed to connect to MySQL: ' . mysqli_connect_error());
}

// Now we check if the data from the login form was submitted, isset() will check if the data exists.
if ( !isset($_POST['username'], $_POST['password']) ) {
	// Could not get the data that should have been sent.
	die ('Please fill both the username and password field!');
}

// Prepare our SQL, preparing the SQL statement will prevent SQL injection.
if ($stmt = $con->prepare('SELECT id, password FROM users WHERE username = ?')) {
	// Bind parameters (s = string, i = int, b = blob, etc), in our case the username is a string so we use "s"
	$stmt->bind_param('s', $_POST['username']);
	$stmt->execute();
	// Store the result so we can check if the account exists in the database.
	$stmt->store_result();
}

if ($stmt->num_rows > 0) {
	$stmt->bind_result($id, $password);
	$stmt->fetch();
	// Account exists, now we verify the password.
	// Note: remember to use password_hash in your registration file to store the hashed passwords.
	if (password_verify($_POST['password'], $password)) {
		// Verification success! User has loggedin!
		// Create sessions so we know the user is logged in, they basically act like cookies but remember the data on the server.
		session_regenerate_id();
		$_SESSION['loggedin'] = TRUE;
		$_SESSION['name'] = $_POST['username'];
		$_SESSION['id'] = $id;
		header('Location: ../index.php');
	} else {
		echo 'Incorrect password!';
	}
} else {
	echo 'Incorrect username!';
}
$stmt->close();

?>

And this is my registration authentication:

<?php

session_start();

// Change this to your connection info.
$DATABASE_HOST = 'localhost';
$DATABASE_USER = 'root';
$DATABASE_PASS = '';
$DATABASE_NAME = 'ccrp_db';
// Try and connect using the info above.
$con = mysqli_connect($DATABASE_HOST, $DATABASE_USER, $DATABASE_PASS, $DATABASE_NAME);
if (mysqli_connect_errno()) {
	// If there is an error with the connection, stop the script and display the error.
	die ('Failed to connect to MySQL: ' . mysqli_connect_error());
}

// Now we check if the data was submitted, isset() function will check if the data exists.
if (!isset($_POST['first_name'], $_POST['last_name'], $_POST['email'], $_POST['username'], $_POST['password'])) {
	// Could not get the data that should have been sent.
	die ('Please complete the registration form');
}
// Make sure the submitted registration values are not empty.
if (empty($_POST['first_name']) || empty($_POST['last_name']) || empty($_POST['email']) || empty($_POST['username']) || empty($_POST['password'])) {
	// One or more values are empty.
	die ('All fields are required for the form to be submitted');
}

if (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
	die ('The email address provided is invalid');
}

if (preg_match('/[A-Za-z0-9]+/', $_POST['username']) == 0) {
    die ('The username provided is invalid');
}

if (strlen($_POST['password']) > 20 || strlen($_POST['password']) < 5) {
	die ('Password must be between 5 and 20 characters');
}

if ($_POST['password'] != $_POST['confirm_pwd']) {
    die ('The two password provided do not match.');
}

// We need to check if the account with that username exists.
if ($stmt = $con->prepare('SELECT id, password FROM users WHERE username = ?')) {
	// Bind parameters (s = string, i = int, b = blob, etc), hash the password using the PHP password_hash function.
	$stmt->bind_param('s', $_POST['username']);
	$stmt->execute();
	$stmt->store_result();
	// Store the result so we can check if the account exists in the database.
	if ($stmt->num_rows > 0) {
		// Username already exists
		echo 'Another account with this username already exists';
	} else {
		// Username doesnt exists, insert new account
if ($stmt = $con->prepare('INSERT INTO users (role_id, first_name, last_name, email, username, password) VALUES (?, ?, ?, ?, ?, ?)')) {
	// We do not want to expose passwords in our database, so hash the password and use password_verify when a user logs in.
	$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
	$stmt->bind_param('ssssss', $_POST['role_id'], $_POST['first_name'], $_POST['last_name'], $_POST['email'], $_POST['username'], $password);
	$stmt->execute();
	header('Location: ../index.php');
} else {
	// Something is wrong with the sql statement, check to make sure users table exists with all 3 fields.
	echo 'There was a problem creating this account. Contact your Network Administrator.';
}
	}
	$stmt->close();
} else {
	// Something is wrong with the sql statement, check to make sure users table exists with all 3 fields.
	echo 'There was a problem creating this account. Contact your Network Administrator.';
}
$con->close();

?>

just store stuff in $_SESSION

It doesn’t sound or look that easy to me… I know I have to use them, but I’m not sure where or how to use them to make it work.

It depends on what you want to do with them. You don’t need to use them at all if you don’t need to use them.

The SESSION array can bring variable values from one page to another. You need to think of how to use them in your code where you need “if this - then this” conditionals. Similar to how you can use GET / POST values. A difference being that instead of user supplied values, they come from the SESSION array.

My goal is to create a standard login system with user roles so that I can prevent users with certain roles from accessing certain web pages. The session should be created when the user logs in and the session should be destroyed when the user logs out. I can see $_SESSION variables being created in my registration auth code, but I don’t know what else I need to do or if I’m doing it right.

SESSION variables can be anything, but looking at what’s there as an example

		$_SESSION['loggedin'] = TRUE;
		$_SESSION['name'] = $_POST['username'];
		$_SESSION['id'] = $id; 

The boolean can be, and often is, used as a way to control “let the user see / do this” based on whether or not they are logged in as a registered user.

The string might be used to display a more personal type of “Hi user”.*

The numeric can be, and often is, used as a way to control “let the user see / do this” based on what capabilities the particular user has.

* the name might also be used in conditional checks, but they would need to be unique which may or may not be the case.

So, how then do I control web page access using these sessions? Is it literally just as easy as including the session variables in the files? I don’t think a session is being made as I tried this before and I kept getting redirected back to my login page.

My login page is this:

<!DOCTYPE html>
<html lang="en">

<head>

  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">

  <title>CCRP - Database Login</title>

  <!-- Custom fonts for this template-->
  <link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
  <link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">

  <!-- Custom styles for this template-->
  <link href="css/sb-admin-2.min.css" rel="stylesheet">

</head>

<body class="bg-gradient-primary">

  <div class="container">

    <!-- Outer Row -->
    <div class="row justify-content-center">

      <div class="col-xl-10 col-lg-12 col-md-9">

        <div class="card o-hidden border-0 shadow-lg my-5">
          <div class="card-body p-0">
            <!-- Nested Row within Card Body -->
            <div class="row">
              <div class="col-lg-6 d-none d-lg-block"><img src="img/rpwall.jpg" width="485" height="600"></div>
              <div class="col-lg-6">
                <div class="p-5">
                  <div class="text-center">
                    <h1 class="h4 text-gray-900 mb-4">Welcome</h1>
                  </div>
                  <form class="user" method="post" action="api/login_auth.php">
                    <div class="form-group">
                      <input type="text" class="form-control form-control-user" name="username" placeholder="Username" autocomplete="off">
                    </div>
                    <div class="form-group">
                      <input type="password" class="form-control form-control-user" name="password" placeholder="Password" autocomplete="off">
                    </div>
                    <div class="form-group">
                      <div class="custom-control custom-checkbox small">
                        <input type="checkbox" class="custom-control-input" id="customCheck">
                        <!-- <label class="custom-control-label" for="customCheck">Remember Me</label> --->
                      </div>
                    </div>
                    <button type="submit" name="login-submit" class="btn btn-primary btn-user btn-block" style="background-color:#a40000; border-color:#a40000;">Login</button>
                    <hr>
                    <!-- <a href="index.html" class="btn btn-google btn-user btn-block">
                      <i class="fab fa-google fa-fw"></i> Login with Google
                    </a>
                    <a href="index.html" class="btn btn-facebook btn-user btn-block">
                      <i class="fab fa-facebook-f fa-fw"></i> Login with Facebook
                    </a> -->
                  </form>
                  <div class="text-center">
                    <a class="small" href="forgot_password.php">Reset Password</a>
                  </div>
                  <div class="text-center">
                    <a class="small" href="register.php">Create an Account</a>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>

      </div>

    </div>

  </div>

  <!-- Bootstrap core JavaScript-->
  <script src="vendor/jquery/jquery.min.js"></script>
  <script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>

  <!-- Core plugin JavaScript-->
  <script src="vendor/jquery-easing/jquery.easing.min.js"></script>

  <!-- Custom scripts for all pages-->
  <script src="js/sb-admin-2.min.js"></script>

</body>

</html>

SESSIONs are PHP but you posted only HTML as the login page. I suppose you could add something like an “if logged in don’t show this” around the HTML … but …

The form has:
action="api/login_auth.php"
the problem suggests that the code in that file isn’t setting a “logged in” correctly.

I see

header('Location: ../index.php'); 

If you are ending up on the login page instead of the index page login_auth.php needs scrutiny. If the login form page and the index page are one and the same page, try adding the conditional checking PHP code to the file.

The login API is currently being used in login_auth.php since I have error code written inside this file. If I move it over, the login form changes to this error message: Please fill both the username and password field

I would like to have the error messages displayed on the same page if I can figure that out as well.

I think it would be easier to focus on one problem at a time, starting with solving the most crucial and worrying about improvements later.

Try this:
After logging out and clearing all your cookies and cache, go to the login form page.

Everything looks good at that point?

Am I correct that the problem is when you log in you end up back at the login form page instead of the index page?

At the moment, my login will work since I am redirected to the index page. I removed this code before creating this topic. However, there are no session variables other than session_start(); on the index page. Other than that, I do not believe a user session is being made. I tried this code on the index page, but I got redirected to the login page every time, which is why I removed it:

if(isset($_SESSION['loggedin'])) {
 // Not sure what to put here
} else { 
header(Location: 'login.php');
}

Both session and header need to be “before output is sent”, so as long as you are not getting syntax errors the problem is possibly a logic error.

If you temporarily replace the header line eg.

if(isset($_SESSION['loggedin'])) {
 // Not sure what to put here
} else { 
// header(Location: 'login.php'); 
var_dump($_SESSION['loggedin']); // debugging only 
}

do you get NULL, “undefined”, boolean FALSE or the boolean TRUE you expect it to be?

This is what I see after adding the code to the index page:

Notice: Undefined index: loggedin in C:\xampp\htdocs\ccrp\index.php on line 8 NULL

However, when I login using the login page, that error disappears until I logout and reload the page? That doesn’t make sense to me…

The last posted set of symptoms indicate that the login/session variable is (currently) working. The undefined index error, when not logged in, and the absence of that message, when you are logged in, is the expected result. I think the previous statement, of ‘i got redirected to the login page every time’, was a communication problem in describing what was occurring, and likely meant ‘when initially visiting the site.’ Based on other things the OP has asked about in this thread (and at least one other that I can recall about displaying user information on a page), his expectation is you can visit the index page while not logged in and see/do somethings.

I think there’s a ‘definition/expectation’ issue going on. Who can access the index page? Only logged in users, in which case you would have a header() redirect in the session checking code on the index page, OR all users, but you want to allow a logged in user to see/do different things depending on permissions they have been given, in which case you would NOT have a header() redirect in the session checking code on the index page.

BTW - Every header() redirect must have an exit/die statement after it to STOP program execution. A header() statement doesn’t stop code execution and if you had some code following a header() redirect that was modifying session variables, it could account for unexpected operation of your code.

You shouldn’t be accepting an external value from the user during registration that defines the user’s role. The role should be set, on the server, to be a default value, and the only way it can be set to anything else is through an existing site owner/administrator/moderator changing the value.

1 Like

Ok, let me explain:

The only people who should be able to access the index page is any logged in user regardless of the user role they have been assigned. The index page is essentially the welcome page (or the starting point) of the main application. If the user is not logged in, access is denied. Also, this does not apply to just the index page, it will apply to all pages throughout the app.

The same can be said after registration. After an account is created, a session should start with the newly created user until they logout in which case their session should be destroyed and the login page should appear.

On all of the site pages where it says Welcome, User!, I would like to change the User part to the user’s first name. I assume a session variable would be the best option (if not the only option) in this case, but I need to figure out how to prevent the undefined index error from appearing.

@mabismad You are right. I did have a thread posted on this once before, but I had to rewrite the code as none of it was making much sense and the thread was getting quite long.

If that’s the case, how do I setup user roles for my application? What is the best way to do it? Can a MySQL table or database control this?

Hopefully, this clears a few things up. Let me know if you still have questions.

So, continuing with the operation of the login/session variable. In reply #11 in this thread, you stated -

Every time what? What exact point in the process did that occur at and have you made any changes to the posted code up to when you made that statement? You need to describe, step by step, what it takes to reproduce that specific problem.

In addition to having no exit/die statement after the header() redirects, another common issue with sessions not ‘working’ is changing the host-name/sub-domain (a WWW. vs no WWW.) in URL’s, either by having links, bookmarks, short-cuts, redirects, form action attributes, htaccess rules, … that are not consistently using the same variation of the URL (you can and should configure the session cookie domain setting so that it matches all variations of your host-name, but you should also find what’s causing the current problem and fix it.)

Another common issue is assigning a value using one = vs comparing a value using two == in a conditional statement. I suspect you may have some code on the index page doing this and you can initially visit and see the page as a logged in user, but after that first visit, you end up being logged out and any navigation to or refresh of that page causes a redirect to the login form page?

Your login form processing page has a redirect to the index page. Does that occur when you successfully log in?

This reply contained code on the index page that I had tried once before. Since then, I have solved this issue by removing it because I kept getting redirected to the login page every time the redirect to the index page was made.

The login_auth code is currently set to redirect me to the index page after login is complete. Once that happens, the PHP code from the index.php (listed below) is executed. I had tried the code above first, but it didn’t work because each time I logged in from the login page, the page would simply reload and the redirect to the index page wouldn’t work. I have already solved this by replacing it with this code instead:

<?php

session_start();

if(!isset($_SESSION['loggedin'])) {
	//var_dump($_SESSION['loggedin']);
	header('Location: login.php');
	exit();	
}

$_SESSION['name'] = $_POST['first_name'];

?>

I commented out the var_dump(); line for now making sure the session works. You were correct in your previous reply when you stated:

So, if the session is working, how do I create user roles and permissions and what is the best way to do this? Also, how do I grab the user’s first name to display on the app’s pages?

1 Like

Onto the next issue(s).

The only piece of user information you should store in a session variable, when the user logs in, is the user_id. You can also use this as the ‘logged in’ flag. It will either be set and contain the user id, or it won’t be set.

You should query on each page request to get any other user information/permissions, and store these in an appropriately named regular program variable. You would then test/use the content of this variable in the rest of the code on the page. The reason for doing this is so that any change/edit made to the user information/permissions will take effect immediately (on the very next page request.) If you store this in session variable(s), in the login code, it won’t get updated until the next login (as I like to tease - please mr. spammer, log out and back in so that you will get banned?)

You should use an existing system, so that you aren’t reinventing the wheel.

Short version - you would define named user roles in a database table. This would assign role ids to those. You would define named permissions in a database table. This would assign permission ids to those. You would have a table holding role_permission data that would store a row for each permission_id for each role_id. You can assign multiple role ids to any user, so there would be a user_role table. You would query to get the list of permission_ids for the current user (stored in an array), then for any permission specific output or operation on a page, test if the current user has the permission_id needed (see in_array()) for the code to produce that output or perform the operation. You would have a super/owner-administrator page to create/edit/delete the roles, permissions, and role_permission data. The super/owner-administrator can also create/edit/delete user_role data and would create regular-administrators, who would have permission ids set up for the regular-administrator role, to let them create/edit/delete user_role data for the next lower level of users, …

How do I swap out the username from Welcome, User to Welcome, and the user’s first name? I have this line included in my index page, but it is still providing me an undefined index:

$_SESSION['name'] = $_POST['first_name'];

For which one, and where do each of those variables come from?