Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

PHP PHP User Authentication Adding Authentication to Your Application Require Authentication

Able to require authentication, but if not logged in, get white screen

Having some issues with authentication. I have required requireAuth() on some of the pages, and so if the user is logged in then they are able to see the file. However if the user is not logged in, instead of being redirected to /dev/ (last line of text), I just see a white sheet.

var_dumping() shows that isAuthenticated() is false if the user is not logged in and true if the user is logged in so I believe the part that is not working is the $accessToken. I have included a print_r() of the $accessToken to see if anyone knows what I am missing.

Any help is appreciated.

function isAuthenticated() {
    if (!request()->cookies->has('access_token')) {
        return false;
    }

    try {
        \Firebase\JWT\JWT::$leeway = 1;
        \Firebase\JWT\JWT::decode(
             request()->cookies->get('access_token'),
             getenv('SECRET_KEY'),
             ['HS256']
         );
         return true;
     } catch (\Exception $e) {
         echo "error is " . $e->getMessage();
     }
 }

 function requireAuth() {
     if(!isAuthenticated()) {
         $accessToken = new \Symfony\Component\HttpFoundation\Cookie('access_token',     'Expired', time()-3600, '/dev/', getenv('COOKIE_DOMAIN'));
         //print_r($accessToken);
         redirect('/dev/',['cookies' => [$accessToken]]);
     }
 }

print_r($accessToken)

Symfony\Component\HttpFoundation\Cookie Object
 (
     [name:protected] => access_token
     [value:protected] => Expired
     [domain:protected] => localhost
     [expire:protected] => 1490307817
     [path:protected] => /dev/
     [secure:protected] => 
     [httpOnly:protected] => 1
     [raw:Symfony\Component\HttpFoundation\Cookie:private] => 
     [sameSite:Symfony\Component\HttpFoundation\Cookie:private] => 
 )

Also here is the redirect()

function redirect($path, $extra = []) {
    $response = \Symfony\Component\HttpFoundation\Response::create(null, \Symfony\Component\HttpFoundation\Response::HTTP_FOUND, ['Location' => $path]);
    if (key_exists('cookies', $extra)) {
        foreach ($extra['cookies'] as $cookie) {
            $response->headers->setCookie($cookie);
        }
    }
    $response->send();
    exit;
}

I also noticed if I use the Symfony sessions (in the tutorial video after) I also get an error. Not sure if the problem is linked. It seems the problem is with the $session->start() method..

This is the code in my bootstrap.php file

require_once(__DIR__.'/../../vendors/vendor/autoload.php');
require_once('functions.php');

$dotenv = new Dotenv\Dotenv(__DIR__);
$dotenv->load();

$session = new Symfony\Component\HttpFoundation\Session\Session();
try {
    $session->start();
 } catch(Exception $e) {
    echo "getMessage() - ".$e->getMessage();
    echo "<br>";
    echo "getline() - " . $e->getLine();
    echo "<br>";
    echo "getCode() - " . $e->getCode();
    echo "<br>";
    echo "getFile() -  " . $e->getFile();
    echo "<br>";
    echo "getTrace() - ".$e->getTrace();
    echo "<br>";
    echo "getTraceAsString() - ".$e->getTraceAsString();
}

And it outputs:

getMessage() - Failed to start the session because headers have already been sent by     "/Users/Oli/Desktop/gripup/dev/login.php" at line 4.
getline() - 134
getCode() - 0
getFile() - /Users/Oli/Desktop/gripup/dev/vendors/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php
getTrace() - Array
getTraceAsString() - #0 /Users/Oli/Desktop/gripup/dev/vendors/vendor/symfony/http-foundation/Session/Session.php(71): Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage->start() #1 /Users/Oli/Desktop/gripup/dev/app/inc/bootstrap.php(12): Symfony\Component\HttpFoundation\Session\Session->start() #2 /Users/Oli/Desktop/gripup/dev/login.php(5): require_once('/Users/Oli/Desk...') #3 {main}

5 Answers

Alena Holligan
STAFF
Alena Holligan
Treehouse Teacher

I want to verify that your vendor folder in in Desktop/gripup/dev/ ?

login.php is where there error is coming from, can I see that file? is the bootstrap file included at there very top? should be line 2 right after <?php

No its not, the vendor file is within the vendors folder (Desktop/gripup/dev/vendors/vendor). This is being made locally on phpStorm and this is where it installed the folder for some reason. I could try and remove this and manually install composer into another directory. I have also just noticed there is a composer.phar file within dev/app/inc/ and the vendor folder is within dev/vendors/vendor. Not sure if thats normal.

Are you sure its login.php? Because with my bootstrap.php file throwing these errors, I can see exactly the same error being thrown on register.php but it says

getMessage() - Failed to start the session because headers have already been sent by "/Users/Oli/Desktop/gripup/dev/register.php" at line 4.

Here is an image to show its layout. The dev/ part is the main home page with front end website. The app/ part is where I was planning on having the web app.

alt text

here is login.php

<!DOCTYPE html>
<html lang="en">
<?php include("app/inc/html/header.php");
require_once('app/inc/bootstrap.php' );
//requireAuth(); <-- if I uncomment this I just get a white screen
// Also tried try catching it but no luck.  -->
?>

<div class="main-body">
    <div id="wrap">
        <div class="container">
            <?php
        if (request()->cookies->has('access_token')) {
            echo "<h2>You are already logged in</h2>";
        }?>
        <div class="well col-sm-6 col-sm-offset-3">
            <form class="form-signin" method="post" action="app/inc/actions/doLogin.php">
                <h2 class="form-signin-heading">Please sign in</h2>
                <?php //print display_errors(); ?>
                <?php //print display_success(); ?>
                <label for="inputEmail" class="sr-only">Email address</label>
                <input type="email" id="inputEmail" name="email" class="form-control" placeholder="Email address" required autofocus>
                <br>
                <label for="inputPassword" class="sr-only">Password</label>
                <input type="password" id="inputPassword" name="password" class="form-control" placeholder="Password" required>
                <br>
                <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
            </form>
        </div>
        </div>
    </div>
</div>
</html>
Alena Holligan
STAFF
Alena Holligan
Treehouse Teacher

you need to put the

<?php include("app/inc/html/header.php");
require_once('app/inc/bootstrap.php' );

BEFORE any HTML output, so BEFORE

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

Thanks your right, not sure how i managed that. That fixes the errors showing up, but still stuck with this problem with redirect().

When I try to login I still get a blank page. I have even downloaded the files and replaced my functions with your functions and also the login page and doLogin. Sadly I am just getting a white screen.

I have included all the files i believe are involved in the login. Been trying all day to get my head round it but nothing. Any help would be really really appricated

login.php

require_once('app/inc/bootstrap.php' );
?>

<div class="main-body">
<div id="wrap">
    <div class="container">
        <?php
        if (request()->cookies->has('access_token')) {
            echo "<h2>You are already logged in</h2>";
        }?>
        <div class="well col-sm-6 col-sm-offset-3">
            <form class="form-signin" method="post" action="app/inc/actions/doLogin.php">
                <h2 class="form-signin-heading">Please sign in</h2>
                <?php //print display_flash_error_success(); ?>
                <label for="inputEmail" class="sr-only">Email address</label>
                <input type="email" id="inputEmail" name="email" class="form-control" placeholder="Email address" required autofocus>
                <br>
                <label for="inputPassword" class="sr-only">Password</label>
                <input type="password" id="inputPassword" name="password" class="form-control" placeholder="Password" required>
                <br>
                <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
            </form>
        </div>
    </div>
</div>
</div> 

doLogin.php (within Users my user's id is called user_id, hence $user['user_id'] rather than $user['id'])

require __DIR__ .'/../bootstrap.php';

$user = findUserByEmail(request()->get('email'));
if (empty($user)) {
    $session->getFlashBag()->add('error', 'Username was not found');
    redirect('/login.php');
}

if (!password_verify(request()->get('password'), $user['password'])) {
    $session->getFlashBag()->add('error', 'Invalid Password');
    redirect('/login.php');
}

$expTime = time() + 7200;

$jwt = \Firebase\JWT\JWT::encode([
    'iss' => request()->getBaseUrl(),
    'sub' => "{$user['user_id']}",
    'exp' => $expTime,
    'iat' => time(),
    'nbf' => time(),
    'is_admin' => $user['role_id'] == 1
], getenv("SECRET_KEY"),'HS256');

$accessToken = new Symfony\Component\HttpFoundation\Cookie('access_token', $jwt, $expTime, '/', getenv('COOKIE_DOMAIN'));

$session->getFlashBag()->add('success', 'Successfully Logged In');
redirect('/dev/',['cookies' => [$accessToken]]);

bootstrap.php

require_once(__DIR__.'/../vendor/autoload.php');
require_once __DIR__ . '/functions.php';
require_once __DIR__ . '/connection.php';

$dotenv = new Dotenv\Dotenv(__DIR__);
$dotenv->load();

$session = new \Symfony\Component\HttpFoundation\Session\Session();
$session->start();

?>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" crossorigin="anonymous"></script>
<link rel="stylesheet" type="text/css" href="/app/inc/css/admin.css">

within functions.php

function redirect($path, $extra = []) {
    $response = \Symfony\Component\HttpFoundation\Response::create(null, \Symfony\Component\HttpFoundation\Response::HTTP_FOUND, ['Location' => $path]);
    if (key_exists('cookies', $extra)) {
        //print_r($extra); (see below)
        foreach ($extra['cookies'] as $cookie) {
            //echo $cookie; (see below)
            $response->headers->setCookie($cookie);
        }
    }
    $response->send();
    exit;
}

echo $cookie

ACCESS_TOKEN = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJcL2RldlwvYXBwXC9pbmNcL2FjdGlvbnNcL2RvTG9naW4ucGhwIiwic3ViIjoiOSIsImV4cCI6MTQ5MDczNjU0NiwiaWF0IjoxNDkwNzI5MzQ2LCJuYmYiOjE0OTA3MjkzNDYsImlzX2FkbWluIjpmYWxzZX0.AfMXIX8VqFU6zVAM36y6Sg_SrRHTRHplJ8Op0yOv-J4; expires = Thu, 28-Mar-2017 9:29:06 p.m. GMT; path = /; domain = localhost; httponly

print_r($extra)

Array
(
[cookies] => Array
    (
        [0] => Symfony\Component\HttpFoundation\Cookie Object
            (
                [name:protected] => access_token
                [value:protected] => eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJcL2RldlwvYXBwXC9pbmNcL2FjdGlvbnNcL2RvTG9naW4ucGhwIiwic3ViIjoiOSIsImV4cCI6MTQ5MDczNjc2NSwiaWF0IjoxNDkwNzI5NTY1LCJuYmYiOjE0OTA3Mjk1NjUsImlzX2FkbWluIjpmYWxzZX0.RZSjXnxV96KGvDlGS_yEl_JfCZ6uS7bijPGwU604CZA
                [domain:protected] => localhost
                [expire:protected] => 1490736765
                [path:protected] => /
                [secure:protected] => 
                [httpOnly:protected] => 1
                [raw:Symfony\Component\HttpFoundation\Cookie:private] => 
                [sameSite:Symfony\Component\HttpFoundation\Cookie:private] => 
            )

    )
)

Here is an updated structure

alt text

Alena Holligan
STAFF
Alena Holligan
Treehouse Teacher

does it show "login.php" in the url for the blank page?

Do you have all errors turned on? https://teamtreehouse.com/library/basic-error-handling-in-php

Can you echo something at the VERY top of the login.php page?

Once I enter the correct or incorrect details and click login, the url changes to http://localhost:8888/dev/app/inc/actions/doLogin.php (which it should), but is blank.

Yes all errors are turned all.

The login.php page is ok I believe. It loads an echo at the top and the bottom of the page.

If I use the correct login info, when I login and add an echo to doLogin.php (like below), it does echo "test". I have also var_dumped the $accessToken below. If i enter the information in incorrectly, so it should redirect to the login.php page in either of the first 2 if statements, then it again still is a blank screen and is stuck on the doLogin.php page. So it seems like the trouble is with the redirect.

require __DIR__ .'/../bootstrap.php';

$user = findUserByEmail(request()->get('email'));
if (empty($user)) {
    $session->getFlashBag()->add('error', 'Username was not found');
    redirect('/login.php');
}

if (!password_verify(request()->get('password'), $user['password'])) {
    $session->getFlashBag()->add('error', 'Invalid Password');
    redirect('/login.php');
}

$expTime = time() + 7200;

$jwt = \Firebase\JWT\JWT::encode([
'iss' => request()->getBaseUrl(),
'sub' => "{$user['user_id']}",
'exp' => $expTime,
'iat' => time(),
'nbf' => time(),
'is_admin' => $user['role_id'] == 1
], getenv("SECRET_KEY"),'HS256');

$accessToken = new Symfony\Component\HttpFoundation\Cookie('access_token', $jwt, $expTime, '/', getenv('COOKIE_DOMAIN'));
var_dump($accessToken);
$session->getFlashBag()->add('success', 'Successfully Logged In');
echo "test";
redirect('/dev/',['cookies' => [$accessToken]]);

var_dump($accessToken);

object(Symfony\Component\HttpFoundation\Cookie)#19 (9) {
  ["name":protected]=>
  string(12) "access_token"
  ["value":protected]=>
  string(248)     "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJcL2RldlwvYXBwXC9pbmNcL2FjdGlvbnNcL2RvTG9naW4ucGhwIiwic3ViIjoiOSIsImV4cCI6MTQ5MDgxNTQ5NSwiaWF0IjoxNDkwODA4Mjk1LCJuYmYiOjE0OTA4MDgyOTUsImlzX2FkbWluIjpmYWxzZX0.52bhrFjlWDLtJbagQRENO7sMOV5agiXaMz341s6-KAg"
  ["domain":protected]=>
  string(9) "localhost"
  ["expire":protected]=>
  int(1490815495)
  ["path":protected]=>
  string(1) "/"
  ["secure":protected]=>
  bool(false)
  ["httpOnly":protected]=>
  bool(true)
  ["raw":"Symfony\Component\HttpFoundation\Cookie":private]=>
  bool(false)
  ["sameSite":"Symfony\Component\HttpFoundation\Cookie":private]=>
  NULL
}
test

Right, I have worked it out.

I re-created all the files and it seems to be working. Not sure if there was a trailing white space or if the html within the bootstrap file (even though it was after the ending php tag):

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" crossorigin="anonymous"></script>
<link rel="stylesheet" type="text/css" href="/app/inc/css/admin.css">

Thanks so much for your help! Seems this time it was a silly annoying issue, but I have learnt a lot debugging this one.

Alena Holligan
Alena Holligan
Treehouse Teacher

Great job! Debugging is a valuable skill for any developer :) I'm hoping to put together a specific debugging course in the not too distant future :)