Using PHP sessions with Axios

2022 . Jul . 10 / Web DevelopmentProgramming

The Reason

A small project that I've being coding recently was required to treat each PHP file as a REST endpoint, but still mantaining a session for the user info. OAuth2 was out of the table because of the people that would give manteinance to the code. So pure PHP sessions had to be sufficient for this task.

The problem comes when our front-end is not part of the PHP files. What do we have to do then?

The code

We start with the html files with javascript as the front-end just like:

session.html
<!DOCTYPE html>
<html>
    <head>
        <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

        <script>
            function startSession() {
                console.log('startSession');
            }

            function getSession() {
                console.log('getSession');
            }

            function closeSession() {
                console.log('closeSession');
            }
        </script>
    </head>

    <body>
        <button onclick="startSession()">Start</button>

        <br />

        <button onclick="getSession()">Get</button>

        <br />

        <button onclick="closeSession()">Close</button>
    </body>
</html>

PHP

What our endpoints will do is just start the session and save a value to it, get that value from session, and destroy the session.

session-start.php
<?php
session_start();
$_SESSION['SOME_KEY'] = $_GET['value'];
session-get.php
<?php
session_start();
echo $_SESSION['SOME_KEY'];
session-close.php
<?php
session_start();
session_destroy();

Axios

Using axios to make the requests to each of our endpoints.

function startSession()
axios
    .post('http://localhost:7070/session-start.php?value=Hello,World!')
    .then(response => {
        console.log(response);
    });
function getSession()
axios
    .get('http://localhost:7070/session-get.php')
    .then(response => {
        console.log(response);
    });
function closeSession()
axios
    .delete('http://localhost:7070/session-close.php')
    .then(response => {
        console.log(response);
    });

That's it, we got our requests and responses working, as easy as it gets. The session in PHP is saving the value=Hello,World!, and we are getting it in another endpoint, and can be destroyed at the third one.

CORS

Our simple example worked wonders because the html file and the php files are placed in the same domain localhost:7070, what will happen when they are not?

So CORS happen. This is a little bit complex subject that is not going to be covered in this example, but I'm going to share some code that I've been using during development as a workaround, without intending to use it in production.

session-cors.php
// Set the front-end domain/origin
header('Access-Control-Allow-Origin: http://localhost:8585');
// Allow all the methods
header('Access-Control-Allow-Methods: GET,HEAD,POST,PUT,DELETE,CONNECT,OPTIONS,TRACE,PATCH');
// For the preflight request
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
    http_response_code(200);
    exit;
}

Then, we just need to require this code at each of our endpoints.

session-*.php
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . "/session-cors.php";
// ...

Now we are not getting more CORS errors, but what is the response at the session-get.php endpoint (calling session-start.php before it)?

response
{data: '', status: 200, statusText: 'OK', headers: {}, config: {},}

The response.data is empty. This is because, now that we are not in the same origin/domain/host, the cookies are not being sent with our requests, including the session one. We have to set the withCredentials option as true in the axios defaults. As per the axios documentation says, it indicates whether or not cross-site Access-Control requests should be made using credentials.

axios.defaults
// Before the functions
axios.defaults.withCredentials = true;
// ...

Still not working, but the message is clear, just set the Access-Control-Allow-Credentials header to true in the session-cors.php file:

session-cors.php
// Before the preflight line
header('Access-Control-Allow-Credentials: true');
// ...

Now the response at the session-get.php endpoint, after calling session-start.php is:

response
{data: 'Hello,World!', status: 200, statusText: 'OK', headers: {}, config: {},}

And, of course, after calling session-close.php we'll get response.data empty again.

Comments
If you have any doubt or question, or know how to improve this post, please feel free to write a comment.