Php login system not working

I am a beginner developer working on a small project and I have to build a simple login system. I watched tutorials on youtube (Codecourse), but unfortunately my code does not work. User can register, information is uploaded to the database but when I try to log in nothing happens.Thanks in advance.

login.php

<?php
require_once 'core/init.php';

if(Input::exists()){
  if(Token::check(Input::get('token'))){

    $validate = new Validate();
    $validation = $validate->check($_POST, array(
      'username' => array('required' => true),
      'password' => array('required' => true)
    ));

    if($validation->passed()){
      $user = new User();

      $remember = (Input::get('remember') === 'on') ? true : false;
      $login = $user->login(Input::get('username'), Input::get('password'), $remember);
      if($login) {
        Redirect::to('index.php');
      } else {
        echo '<p>Sorry, login failed.</p>';
      }
    } else {
      foreach($validation->errors() as $error){
        echo $error, '<br>';
      }
    }

  }
}
?>

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Login System</title>
    <link rel="stylesheet" href="css/style.css">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <body>
    <header>
    <nav id="nav-bar">
      <a href="index.php" id="features" class="nav-link"> Home </a>
      <a href="login.php" id="howit" class="nav-link"> Log In </a>
      <a href="register.php" id="costumes" class="nav-link"> Register </a>
    </nav>
  </header>

  <div class="formsec">
    <form action = "" method="post">
      <div class="field">
        <input type="text" name="username" id="username" autocomplete="off" placeholder="Username">
      </div>

      <div class="field">
        <input type="password" name="password" id="password" autocomplete="off" placeholder="Password">
      </div>

      <div class="field">
      <label for="remember">
        <input type="checkbox" name="remember" id="remember"> Remember Me
      </label>
    </div>

      <input type="hidden" name="token" value="<?php echo Token::generate(); ?> ">
      <input type="submit" value="Log in" class="button">
    </form>
  </div>
</body>

User.php

<?php
class User{
  private $_db,
          $_data,
          $_sessionName,
          $_cookieName,
          $_isLoggedIn;

  public function __construct($user = null){
    $this->_db = DB::getInstance();

    $this->_sessionName = Config::get('session/session_name');
    $this->_cookieName = Config::get('remember/cookie_name');

    if(!$user){
      if(Session::exists($this->_sessionName)){
        $user = Session::get($this->_sessionName);

        if($this->find($user)){
          $this->_isLoggedIn = true;
        } else {
          //logout
        }
      } else {
        $this->find($user);
      }
    }
  }

  public function update($fields = array(), $id = null){

    if(!$id && $this->isLoggedIn()){
      $id = $this->data()->id;
    }

    if(!$this->_db->update('users', $id, $fields)){
      throw new Exception('There was a problem updating.');
    }
  }

  public function create($fields = array()){
    if(!$this->_db->insert('users', $fields)){
      throw new Exception('There was a problem creating an account.');
    }
  }

  public function find($user = null){
    if($user){
      $field = (is_numeric($user)) ? 'id' : 'username';
      $data = $this->_db->get('users', array($field, '=', $user));

      if($data->count()){
        $this->_data = $data->first();
        return true;
      }
    }
    return false;
  }

  public function login($username = null, $password = null, $remember = false){
    if(!$username && !$password && $this->exists()){
      Session::put($this->_sessionName, $this->data()->id);
    } else {
      $user = $this->find($username);
      if($user){
        if($this->data()->password === Hash::make($password, $this->data()->salt)){
          Session::put($this->_sessionName, $this->data()->id);

          if($remember){
            $hash = Hash::unique();
            $hashCheck = $this->_db->get('users_session', array('user_id', '=', $this->data()->id));

            if(!$hashCheck->count()){
              $this->_db->insert('users_session',array(
                'user_id' => $this->data()->id,
                'hash' => $hash
              ));
            }else {
              $hash = $hashCheck->first()->hash;
            }

            Cookie::put($this->_cookieName, $hash, Config::get('remember/cookie_expiry'));

          }
          return true;
        }
      }
    }
    return false;
  }

  public function hasPermission($key){
    $group = $this->_db->get('groups', array('id', '=', $this->data()->group));

    if($group->count()){
      $permissions = json_decode($group->first()->permissions, true);

      if($permissions[$key] === true){
        return true;
      }
    }
    return false;
  }

  public function exists(){
    return (!empty($this->_data)) ? true : false;
  }

  public function logout(){

    $this->_db->delete('users_session', array('user_id', '=', $this->data()->id));

    Session::delete($this->_sessionName);
    Cookie::delete($this->_cookieName);

  }

  public function data(){
    return $this->_data;
  }

  public function isLoggedIn(){
    return $this->_isLoggedIn;
  }

}

When you check step-by-step, how far through your login code does it get? Does Input::exists() pass or fail? Does Token::check() pass or fail? How about Validate::check()? I don’t see the code for any of those, so it’s difficult to offer advice.

As a wild stab in the dark, your form provides $_POST variables because of method="post", but your code checks Input::get() - either that should be Input::post(), or your function has a misleading name.

2 Likes

Speaking for myself, when I have problems like this I introduce php echoes and either echo a message such as ‘calling function’ or ‘form submitted’. I may also echo vars so I can see what value, if any has been assigned. When you say 'nothing happens, with respect something does happen but you need to locate the point at which it stops happening. I find echoes invaluable for this and you can comment them out temporarily or remove completely when finished. In this way you can determine exactly where your script is failing which makes it much easier to find out why, good luck!

1 Like

Well, to me mixing sessions into the login script would be confusing.

I simply do the following:

    public function login(): void
    {
        $sql = "SELECT id, hashed_password FROM " . static::$table . " WHERE username =:username LIMIT 1";

        $user = static::fetch_by_column_name($sql);

        if ($user && password_verify($this->password, $user['hashed_password'])) {
            unset($this->password, $user['hashed_password']);
            session_regenerate_id(); // prevent session fixation attacks
            static::$last_login = $_SESSION['last_login'] = time();
            $this->id = $_SESSION['id'] = $user['id'];
            header("Location: index.php");
            exit();
        }

        static::$error[] = 'Unable to login in!';

    }

then I have this for is the user logged in and/or security check

    public static function is_login($last_login): void
    {
        if (!isset($last_login) || ($last_login + self::MAX_LOGIN_AGE) < time()) {
            header("Location: login.php");
            exit();
        }
    }
    public static function securityCheck()
    {
        static::$searchItem = "id";
        static::$searchValue = $_SESSION['id'];
        $sql = "SELECT security FROM " . static::$table . " WHERE id=:id LIMIT 1";
        return static::fetch_by_column_name($sql);

    }

then on the page I want to have a member page or limited access I do something like the following:

Login::is_login($_SESSION['last_login']);

$user = Login::securityCheck();

/*
 * Only Sysop privileges are allowed.
 */
if ($user['security'] === 'sysop') {
    header("Location: index.php");
    exit();
}

My point is I try to keep my classes simple and my methods (functions) as short as possible.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.