0

I am developing a login/registration system. After following this PHP login system: Remember Me (persistent cookie)

I was able to set the cookie successfully but I am having problem authenticating the current user.

Register.php

if($login)
{
$_SESSION['userid'] = $userid;
$selector = base64_encode(random_bytes(9));
$authenticator = random_bytes(33);
$token = hash('sha256', $authenticator);
$expires = date('Y-m-d\TH:i:s', time() + 864000);
$stmt2 = $pdo->prepare("INSERT INTO auth_tokens 
(selector,token,userid,expires) VALUES (:selector, :token, :userid, 
:expires)");
$stmt2->bindParam(':selector', $selector);
$stmt2->bindParam(':token', $token);
$stmt2->bindParam(':userid', $userid);
$stmt2->bindParam(':expires', $expires);
$stmt2->execute();
setcookie(
    'remember',
     $selector.':'.base64_encode($authenticator),
     time()+86400,
     '/',
     false
);
header("location: ../home.php");
exit();
}

Check.php

This is where the problem is. How to check for the cookie and do somethings

$selector = base64_encode(random_bytes(9));
$authenticator = random_bytes(33);
$token = hash('sha256', $authenticator);
$expires = date('Y-m-d\TH:i:s', time() + 864000);
if(empty($_SESSION['userid']) && !empty($_COOKIE['remember']))
{
$sql = $pdo->prepare("SELECT * FROM auth_tokens WHERE selector = ?");
$sql->bindValue(1, $selector);
$sql->execute();
$row = $sql->fetch();
if (hash_equals($row['token'], hash('sha256', 
base64_decode($authenticator)))) {
 $_SESSION['userid'] = $row['userid'];
// Then regenerate login token as above
$selector = base64_encode(random_bytes(9));
$authenticator = random_bytes(33);
$token = hash('sha256', $authenticator);
$expires = date('Y-m-d\TH:i:s', time() + 864000);

$st = $pdo->prepare("UPDATE auth_tokens SET (selector,token,userid,expires) 
VALUES (:selector, :token, :userid, :expires)");
$st->bindParam(':selector', $selector);
$st->bindParam(':token', $token);
$st->bindParam(':userid', $userid);
$st->bindParam(':expires', $expires);
$st->execute();

setcookie(
        'remember',
         $selector.':'.base64_encode($authenticator),
         time()+86400,
         '/',
         false
);

header('Location: home.php');
exit;
}

I got this - Warning - "hash_equals() Expected known_string to be a string, NULL given"..

What I want

If session does not exist (A user closes the browser), And visits the check.php page, If the cookie is present I want the user to go to home.php page.

P.S

The UPDATE query not updating the auth_token table for the current user.

And the link doesnt say anything about storing userid session but I feel like its necessary. But when a user closes the browser, the session user id is destroyed so I am not sure how this line of code will work $_SESSION['userid'] = $row['userid']; Hence maybe returning NULL as the warning given.

Anyone with code or way to check for authentication on page load using this persistent login approach?.

Ibrahim
  • 99
  • 12

1 Answers1

0

First) hash_equals (http://php.net/manual/en/function.hash-equals.php) expects its first argument ($known_string) to be "something", not null.

Here you are calling it with $row['token']. If it is null, hash_equals returns the error you got.

So:

  • your query did not return any results.
  • you should check that $row['token'] has a value in it before calling hash_equals. Use if (!empty($row['token']) ....
  • then you have to code some logic to do something when there is no token returned from the query. From what I understand of your question, if no token is found you want the user to back to home.php right? So use something like this:

    if (!$empty($row['token']))
    {
        # here put your code if the user HAS an associated token
    {
    else
    {
        # here put the redirection
        header('Location: http://YOURDOMAIN/home.php');
    }
    

Be careful, for the location header to work, NOTHING can be sent to the client before. No echo, no html, nothing!


Second) as you stated in your comment, the selector is randomly created when the user first connects. And you store that number in the cookie you create.

Your check.php page should read the value of the cookie instead of generating a new random number. Otherwise your query will always return nothing. The cookie values are stored in $_COOKIE.

And congrats for using prepared statements!

Nic3500
  • 8,144
  • 10
  • 29
  • 40
  • I think the problem is from this query **$sql = $pdo->prepare("SELECT * FROM auth_tokens WHERE selector = ?");** The selector changes everytime on page reload cause its random so the selector **base64_encode(random_bytes(9));** doesn't match with anything in the selector column. – Ibrahim Jul 15 '18 at 12:44
  • Indeed. In the check.php page, you should use the same selector as when the account was created. That selector could be stored as a cookie value instead of random. – Nic3500 Jul 15 '18 at 15:34