Avatar of Soumen Roy
Soumen RoyFlag for India

asked on 

How to use session variables in php?

I have built a few crude rest apis that return json response. One api accepts POST request and is used to login a user, and another accepts GET request and reads contents for user from database.

If the login occurs successfully, a $_SESSION["uid"] variable is set for checking if the GET response occurs from the same user. Basically, the login api returns a userid from the users database that is stored in the $_SESSION["uid"] variable. The GET request to read contents, gets this userid as a parameter, and if the server finds that the userid received from this request and $_SESSION["uid"] match then the contents are returned.

When I test the two codes using postman, the GET request returns the desired response, however, when I test the same on the browser (logging in manually from the site interface as the login occurs from a POST request) and then request the GET service from the address bar, it returns error message of not having any userid set (I put the error message in the return json to check if(isset($_SESSION["uid"])) is true else return error message).
Following are the codes:

[Login-POST]
   
<?php
        session_start();
        include_once $_SERVER['DOCUMENT_ROOT'].'/settings/ReadIni.php';
        include_once $_SERVER['DOCUMENT_ROOT'].getinivalue('Paths', 'database_connect_location');
        
        if( $_SERVER['REQUEST_METHOD'] == "POST" ) {
            if( $dberror == "" ) {
                if(isset($_SESSION["uid"])) {
                    $json = array("status" => getinivalue('ReturnValues', 'request_failure'), "message" => "Error: ".getinivalue('ReturnValues', 'session_already_running'));
                }
                else
                {
                    $indata = file_get_contents('php://input');
                    $indata = json_decode($indata);
    
                    $password = $indata->pass;
                    $loginid = mysqli_real_escape_string($conn, $indata->loginid);
                    $pass = mysqli_real_escape_string($conn, $password);
    
                    if(($loginid != "") && ($pass != "")) {
                        $qrymailchk = "SELECT * from user_master where user_mail='$loginid'";
                        $qryphonechk = "SELECT * from user_master where user_phone='$loginid'";
    
                        $resmailchk = mysqli_query($conn, $qrymailchk);
                        $resphonechk = mysqli_query($conn, $qryphonechk);
    
                        $row1 = mysqli_fetch_array($resmailchk, MYSQLI_BOTH);
                        $row2 = mysqli_fetch_array($resphonechk, MYSQLI_BOTH);
    
                        if($row1 || $row2) {
                            $dbpass = ($row1) ? $row1['user_pass'] : $row2['user_pass'];
                            if ($pass == $dbpass) {
                                /*$passchk = password_verify($pass, $dbpass);*/
    
                                $_SESSION["uid"] = ($row1) ? $row1['user_code'] : $row2['user_code'];
    
                                $_SESSION["un"] = ($row1) ? $row1['user_name'] : $row2['user_name'];
                                $_SESSION["em"] = ($row1) ? $row1['user_mail'] : $row2['user_mail'];
                                $_SESSION["ph"] = ($row1) ? $row1['user_phone'] : $row2['user_phone'];
    
                                $words = explode(" ",$_SESSION["un"]);
                                $_SESSION["fn"] = $words[0];
    
                                $json = array("status" => getinivalue('ReturnValues', 'request_success'), "UserName" => $_SESSION["un"], "UID" => $_SESSION["uid"]);
    
            //                    $URL = "/services.php";
            //                    echo '<META HTTP-EQUIV="refresh" content="0;URL=' . $URL . '">';
            //                    echo "<script type='text/javascript'>document.location.href='{$URL}';</script>";
            //    
            //                    exit();
                            }
                            else {
                                $json = array("status" => getinivalue('ReturnValues', 'request_failure'), "message" => "Error: ".getinivalue('ReturnValues', 'invalid_credentials'));
                                mysqli_close($conn);
                            }					
                        }
                        else{
                            $json = array("status" => getinivalue('ReturnValues', 'request_failure'), "message" => "Error: ".getinivalue('ReturnValues', 'invalid_credentials'));
                            mysqli_close($conn);
                        }
                    }
                }
            }
            else {
                $json = array("status" => getinivalue('ReturnValues', 'request_failure'), "message" => "Error: ".$dberror);
            }
        }
        else {
            $json = array("status" => getinivalue('ReturnValues', 'request_failure'), "message" => "Error: ".getinivalue('ReturnValues', 'invalid_request_type'), "Required" => getinivalue('ReturnValues', 'request_type_post'));
        }
        
        header('Content-type: application/json');
        echo json_encode($json);    	
    ?>

Open in new window

[Contents-GET]
   
<?php
        session_start();
        include_once $_SERVER['DOCUMENT_ROOT'].'/settings/ReadIni.php';
        include_once $_SERVER['DOCUMENT_ROOT'].getinivalue('Paths', 'database_connect_location');
        
        if( $_SERVER['REQUEST_METHOD'] == "GET" ) {
            if( $dberror == "" ) {
                if(isset($_GET['uid'])) {
                    $uid = $_GET['uid'];
                    if(isset($_SESSION["uid"])) {
                        if($_SESSION["uid"] == $_GET['uid']) {
                            $qry1 = "SELECT device_code from user_device where user_code='".$uid."' and device_active='1'";
                            $res1 = mysqli_query($conn, $qry1);
    
                            $json = array("status" => getinivalue('ReturnValues', 'request_success'), "list_of_devices" => NULL);
    
                            if(mysqli_num_rows($res1)) {
                                $device_list = array();
    
                                while ($devices = mysqli_fetch_array($res1, MYSQLI_BOTH)) {
                                    $qry2 = "SELECT device_name from device_master where device_code='".$devices[0]."'";
                                    $res2 = mysqli_query($conn, $qry2);
    
                                    $row = mysqli_fetch_array($res2, MYSQLI_BOTH);
    
                                    $device_detail = array("device_code" => $devices[0], "device_name" => $row['device_name']);
                                    array_push($device_list, $device_detail);
                                }
    
                                $json["list_of_devices"] = $device_list;	
                            }
                        }
                        else {
                            $json = array("status" => getinivalue('ReturnValues', 'request_failure'), "message" => getinivalue('ReturnValues', 'invalid_userid'));
                        }
                    }
                    else {
                        $json = array("status" => getinivalue('ReturnValues', 'request_failure'), "message" => getinivalue('ReturnValues', 'no_session'));
                    }
                }
                else {
                    $json = array("status" => getinivalue('ReturnValues', 'request_failure'), "message" => getinivalue('ReturnValues', 'input_not_set'));
                }    
            }
            else {
                $json = array("status" => getinivalue('ReturnValues', 'request_failure'), "message" => "Error: ".$dberror);
            }
        }
        else {
            $json = array("status" => getinivalue('ReturnValues', 'request_failure'), "message" => "Error: ".getinivalue('ReturnValues', 'invalid_request_type'), "Required" => getinivalue('ReturnValues', 'request_type_get'));
        }   
        
        header('Content-type: application/json');
        echo json_encode($json);
    ?>

Open in new window


Please suggest what is wrong with the codes or if there is being any problem with how $_SESSION variables are used.

Thanks in advance.
RESTJSONPHP

Avatar of undefined
Last Comment
Soumen Roy
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Here is what you need to know.  While you read this article, I'll take a look at your code.
https://www.experts-exchange.com/articles/11909/PHP-Sessions-Simpler-Than-You-May-Think.html
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

I think the "user" must accept and return cookies for the PHP session to work.  So the cause of error may be external to your scripts.  I don't see anything inherently wrong in your PHP code or session handling.  But it also looks like you're comparing the UID field in the session to the UID field in the GET request, which should be unnecessary if the cookie is working.  For the cookie to work, any accessing script needs to act like a well-behaved web browser, accepting and returning cookies, and possibly following redirects.

This might be easier to debug if you set up the SSCCE - a small script that only considered the issues around the session.  There are a lot of if/else statements in there, and we don't usually write code that way.  For more on why not, make a Google search for "cyclomatic complexity."  Many if/else paths make it difficult to impossible to test code, and we all insist on automated testing these days.
Avatar of Soumen Roy
Soumen Roy
Flag of India image

ASKER

@Ray Paseur

Please give a few minutes time. I am reading your article now.
Avatar of Soumen Roy
Soumen Roy
Flag of India image

ASKER

Thank you for the article..

So, how do I make the cookie work? I find that when I test the code from my localhost, the PHPSESSID cookie for both the login and get content requests are the same. But when I call the same code uploaded to cloud, the PHPSESSID cookies are different for the two requests,

What do I do to fix this issue and how do I make the second and any subsequent calls from the contents or any new APIs created for this site watch the cookie from the login response?
Avatar of Soumen Roy
Soumen Roy
Flag of India image

ASKER

The site works from 'domain.com' and the API call is made to 'www.domain.com'. So when I log into a session from the site(domain.com) interface it returns a cookie but when I call the api(www.domain.com) it returns a differnet cookie and so the cookies returned do not match. However, when running from localhost, both calls are made to 'localhost' and so the cookies returned are same. I have understood this far.
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Interesting.  I'm not sure how the cloud server plays into this, but that seems to be where the issue lies.

Here's my recommendation:  Don't bother with the POST login part of the process.  Instead use an API key that is part of the GET request.  This is the design of all of Google's various APIs, and if it's good enough for them, it's good enough for us, too.

You can add a little more security to an API key if you associate it with an IP address or an HTTP referrer.  For example, if I am authorized to use your service, I would have to provide the API key, and make the request from IP 98.169.66.145.  It's fairly easy to create a bogus HTTP_REFERER string, but it takes a great deal of sophistication to fake the IP address.
Avatar of Soumen Roy
Soumen Roy
Flag of India image

ASKER

Can you please refer some code or article to go through to understand this part?
Don't bother with the POST login part of the process.  Instead use an API key that is part of the GET request.
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

This is important information:
So when I log into a session from the site(domain.com) interface it returns a cookie but when I call the api(www.domain.com)...
PHP session cookies, by default, are not set for the entire domain.  They are only set for the subdomain.  So a session that exists on test.domain.com will not collide with a session set on domain.com or sub.domain.com.  In the article about sessions, look for "RAY_session_cookie_domain.php" and you will find a script that can make the session work across subdomains.
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Yes, this article has information and examples that show how to use an API key in the GET request.
https://www.experts-exchange.com/articles/12239/Introduction-to-Application-Programming-Interfaces.html
Avatar of Soumen Roy
Soumen Roy
Flag of India image

ASKER

Yes, I was just going through that part. But what if don't want all subdomains to read the same cookie, only the 'domain.com' and 'www.domain.com' have to read the same cookie?
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Probably either approach - API key in the request or cross-subdomain session, will solve the problem.  Make a few tests and choose the approach that seems easier to you.  I would probably opt for the API key in the GET request, because this is the closest to a truly RESTful interface and has the least potential for side-effects (expired session, etc).
Avatar of Soumen Roy
Soumen Roy
Flag of India image

ASKER

I will work on this in a few hours time. Thank you for your proposal. Please do respond in case I come up with any more queries.

And thank you for the good article once more!

:)
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

what if don't want all subdomains to read the same cookie...
Then you would have to write some programming to intervene.  You could set the cookie for .domain.com (note the leading dot) and this will let the cookie serve domain.com and anysubdomain.domain.com.  Then you can check the HTTP_HOST value and disallow anything other than domain.com and www.domain.com.
Avatar of Julian Hansen
Can we see your AJAX code that calls the services?

Are you always using www.domain.com or are you mixing www.domain.com and domain.com [i.e. no www]?

Try adding a dump of the SESSION / POST / GET / COOKIE to your error return something like


$json = array('SESSION' => print_r($_SESSION, true), 'COOKIES' => print_r($_COOKIE, true), 'POST' => print_r($_POST, true), 'GET' => print_r($_GET, true))

Open in new window


Also check your console F12 to see how the remote service is being called. Check the request headers and the response headers as well as the parameters sent and response returned.
Another option is to look at JWT (JSON Web Tokens) - where your authentication token is generated on the server and passed back and forth as an Authorization header. Removes the need for cookies.
ASKER CERTIFIED SOLUTION
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Blurred text
THIS SOLUTION IS ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
Avatar of Soumen Roy
Soumen Roy
Flag of India image

ASKER

Sorry for the later reply. I think I may have to do something of that sort.

I wrote a .htaccess file to to redirect all requests to domain.com to www.domain.com, and that solved the issue with the browser. However, now a friend of mine is trying to build an android app using xamarin on visual studio using these APIs and he says he cannot find a session running after login (the json response for no running session is returned).

So, now I think I will rewrite the API and build a real RESTful API.
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

json response for no running session ...
That may happen if his android app does not act like a well-behaved browser, accepting and returning cookies, running JavaScript, and following redirects.  The whole concept of the PHP session came into being when the only thing using HTTP was the browser/server relationship.  There were no mobile devices, and the entire concept of mobile web access came about independently of browser-based web access.  So while the browsers were doing the "right thing" with HTTP cookies, the mobile devices were not necessarily in touch with the way PHP sessions worked.  I think the approach described in the article, where your API handshake includes the API key on every HTTP request, will be a good way forward.
SOLUTION
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

Blurred text
THIS SOLUTION IS ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

There are some similarities, but the API key design that I favor mirrors Google API keys.  I figure if it's good enough for Google, it's good enough for me.  Here is how they do it.
https://developers.google.com/maps/documentation/javascript/get-api-key
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

API Keys are not the same as session management though.

Unless I missed what the question is about (probable given I have lost track of it given the time) but if we are talking about sessions linked to login - not seeing how an API key is going to work here.
Avatar of Soumen Roy
Soumen Roy
Flag of India image

ASKER

Thank you once again for the wonderful articles. I learnt some things out of them and also made the app work with the APIs.
PHP
PHP

PHP is a widely-used server-side scripting language especially suited for web development, powering tens of millions of sites from Facebook to personal WordPress blogs. PHP is often paired with the MySQL relational database, but includes support for most other mainstream databases. By utilizing different Server APIs, PHP can work on many different web servers as a server-side scripting language.

125K
Questions
--
Followers
--
Top Experts
Get a personalized solution from industry experts
Ask the experts
Read over 600 more reviews

TRUSTED BY

IBM logoIntel logoMicrosoft logoUbisoft logoSAP logo
Qualcomm logoCitrix Systems logoWorkday logoErnst & Young logo
High performer badgeUsers love us badge
LinkedIn logoFacebook logoX logoInstagram logoTikTok logoYouTube logo