4

All,

Sorry in advance - I'm not a PHP expert or knowledgeable in design patterns, so this question might be a little basic...

Anyway, I'm working on a web app that will require a login.

My plan is to have something like this:

index.php: this page will contain a simple form that allows users to enter a username and password. The form will POST the inputs to...

login.php: this page will receive the inputs from index.php, and check those credentials against a database. If any of the inputs are missing, or the credentials check fails, the php script will REDIRECT the user back to index.php using:

header('Location: http://www.mydomain.com/index.php');

If the credentials are valid, then login.php creates a session to establish the user's authenticated status:

session_start();
$_SESSION['authenticated'] = true;

Then, it determines what access type the user has. If he has "level 1" access, the script will redirect the user to level1.php using:

header('Location: http://www.mydomain.com/level1.php');

If the user has "level 2" access, the script will redirect the user to level2.php using:

header('Location: http://www.mydomain.com/level2.php');

Finally, when level1.php or level2.php is reached, the first thing they do is check the session. If the user is not authenticated, redirect him back to index.php:

session_start();
if (!isset($_SESSION['authenticated']) {
    header('Location: http://www.mydomain.com/index.php');
} else {
    // proceed to display the page
}

Having this check in level1.php and level2.php will prevent users from accessing that page directly, without logging in.

My first issue is this: this simple logic FAILS the first time through - when level1.php is reached, "isset($_SESSION['authenticated']" ALWAYS returns false, so the user is always redirected back to index.php. If he enters the exact same credentials a second time, the process works as it should.

In short, for reasons I don't understand, it seems the session that's set by login.php is not found by level1.php - I assume because of the redirect. In other words, the check on level1.php seems to fail until/unless a round trip is made to the client's browser.

Since every site that requires login has already solved this problem, this shouldn't be a novel challenge, and their should be a very established pattern for it. How should I handle it?

A related question... I've seen similar questions asked here before, and most answers usually involve a solution in which pages are POSTing back to themselves. This seems a little wierd - ideally, I'd like to have each PHP page perform a specific job:

  • index.php - display a form to capture credentials, then post them to login.php
  • login.php - evaluate the user's credentials, then direct them to the appropriate page
  • level1.php & level2.php - display the appropriate content

Is this a flawed setup? If so, what's the better setup?

And generally - if one page establishes a session, then redirects the user to another page - is there any way that second page can read the session?

There's a great page on Wikipedia about Post/Redirect/Get:

http://en.wikipedia.org/wiki/Post/Redirect/Get

But it's a little conceptual for me - I'd like to see it explained with references to specific pages:

E.g. a form on "page A" POSTs data to "page B", "page B" redirects the user to "page C", etc...

And, I don't understand how it's implemented with sessions, if sessions aren't recognized when using redirects.

Many thanks in advance for any advice and insights.


[UPDATE]

Thanks to Matt Ball's comment, I've refined the issue:

login.php was setting the session and redirecting the user to the next screen:

session_start();
$_SESSION['authenticated'] = true;
header('Location: http://www.mydomain.com/level1.php');

However, when level1.php checked that session, "authenticated" was NOT SET:

session_start();
echo (isset($_SESSION['authenticated']); // returns false

However, if I changed login.php so that the header redirected to a RELATIVE url instead of an absolute one:

session_start();
$_SESSION['authenticated'] = true;
header('Location: level1.php');

Then, level1.php works like I expect:

session_start();
echo (isset($_SESSION['authenticated']); // now returns true

I don't understand why the relative URL makes a difference, but it does. So, at least my immediate issue is resolved.

Many thanks to everyone who commented!


Cheers, Matt Stuehler

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
mattstuehler
  • 9,040
  • 18
  • 78
  • 108
  • 5
    A shot in the dark concerning the session-problem: did you call `session_start();` in level1.php? Another thing: for security reasons, you should not only check if the session is valid in level1...x.php but also if the user's level is sufficient. Otherwise a Level1-user could log in and then call level2.php manually. – Select0r Oct 07 '10 at 13:39
  • 1
    You should use a tool like Firebug+FireCookie to check that the PHPSESSID cookie is actually set when login.php send its headers, and that they exist also afterwards. – SirDarius Oct 07 '10 at 13:40
  • 2
    I think you need to start you session before you redirect back after the user has logged in – Phill Pafford Oct 07 '10 at 13:46
  • 2
    @matt: sessions _definitely_ recognized with redirects. An HTTP redirect is just a message from the server to the browser that says, "Hey, browser, ask me for this other page." – Matt Ball Oct 07 '10 at 13:46
  • @SelectOr - thanks for your advice. I am calling session_start() in level1.php (I just updated my original post to reflect this), so unfortunately, it's not the reason it's failing. But great point about ALSO checking the level access. – mattstuehler Oct 07 '10 at 14:29
  • @Matt Ball - Thanks for your comment. Here's what I noticed... when I redirect using the absolute URL, the session is not recognized. However, when I redirect with a relative URL, it is. Any idea why this might be the case? – mattstuehler Oct 07 '10 at 18:19

2 Answers2

1

Post Redirect Get comes in to play to stop a user resending their POST data if they refresh the page they've been redirected to after submitting a form. When you want to implement a PRG, you should set the HTTP header code to 303 like this:

header('Location: level1.php', 303);
Nev Stokes
  • 9,051
  • 5
  • 42
  • 44
  • Should check to see if POST operation actually happened first. I like this code more, as it is more complete: http://stackoverflow.com/a/4142969/631764 – Buttle Butkus Dec 23 '12 at 03:44
0

If the credentials are valid, then login.php creates a session to establish the user's authenticated status:

I'm shooting in the dark as well but I have the impression that you may output something before the setting the session in login.php. Place session_start as first instruction in every file.

mhitza
  • 5,709
  • 2
  • 29
  • 52