Using PHP sessions with Axios
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:
<!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.
<?php
session_start();
$_SESSION['SOME_KEY'] = $_GET['value'];
<?php
session_start();
echo $_SESSION['SOME_KEY'];
<?php
session_start();
session_destroy();
Axios
Using axios to make the requests to each of our endpoints.
axios
.post('http://localhost:7070/session-start.php?value=Hello,World!')
.then(response => {
console.log(response);
});
axios
.get('http://localhost:7070/session-get.php')
.then(response => {
console.log(response);
});
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.
// 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.
<?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)?
{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.
// 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:
// 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:
{data: 'Hello,World!', status: 200, statusText: 'OK', headers: {…}, config: {…}, …}
And, of course, after calling session-close.php
we'll get response.data
empty again.