Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

rhand's avatar
Level 6

browser tabs shared session data causes loading wrong data

We realized we have a bug loading data in a secondary tab while project data in the first tab is still open. The second project then loads data from the project in the first tab. This is because we use session data and that is shared by both browser tabs.

This is the code

public static function saveProjectFromJSON($json)
    {
        $project = json_decode($json);
        $currentProject = self::getProjectFromSession();

        ....

        return $currentProject->save();
    }

and it uses getProjectFromSession:

 public static function getProjectFromSession()
    {
        $projectId = session('currentproject');

        return self::find($projectId);
    }

I understood PHP and sessions works this way.

So we could use something like

public static function saveProjectFromJSON($json)
    {
        $attrs = json_decode($json);
        $project = self::find($attrs->id);

        $project->json = json_encode($attrs);

        return $project->save();
    }

and replace getProjectFromSession everywhere and use database data. But then we got into issues with urls loading project id and allowing manual change of id to check for other projects not even under the user's id due to code like

// Get Pages
axios.get(`/editor/get-pages?project=${PROJECT_ID}`)
    .then(res => {
        this.$store.dispatch('editor/setPages', res.data.data)
    })
    .catch(() => {});

NB const PROJECT_ID = {{ $project->id }}

no longer using the auth check in the route

...
Route::prefix('editor')->middleware('auth', 'subcheck')->group(function () {
...
Route::get('get-pages', [Editor\ProjectController::class, 'getPages']);
...

So how can I disallow loading data form another project in a new tab using a different project AND disallow access via a url likehttps://site/editor?project=22 just by changing the number? The session check causes issues for loading data when using multiple tabs, but now without it I am giving access to other projects by other users..

0 likes
8 replies
jlrdw's avatar

You need to use a different browser. Not tab.

1 like
rhand's avatar
Level 6

@jlrdw Yes, when a different browser is used there is no issue with loading project data. But.. some users open a new tab, load a second project to work on. And then we get issues with the wrong data loading in. That is why we added a new branch where we use database project ID data. But when we use this change everywhere including loading a project we have the issues that the middleware no longer works.

Perhaps we should only use project id check in database for that one method

public static function saveProjectFromJSON($json)
    {
        $attrs = json_decode($json);
        $project = self::find($attrs->id);

        $project->json = json_encode($attrs);

        return $project->save();
    }

and keep using

 public static function getProjectFromSession()
    {
        $projectId = session('currentproject');

        return self::find($projectId);
    }

in other methods for loading the project and so on . Cause now

 axios.get(`/editor/get-pages?project=${PROJECT_ID}`)
                .then(res => {
                    this.$store.dispatch('editor/setPages', res.data.data)
                })
                .catch(() => {});
        },

adds project id as string to url and that string / id I can change and open projects I should not be able to open with that user.

Or we need to block opening another project in another tab somehow showing: you are already working on a project, please save and leave before working on another one... But for that I would also need to see how I could do that.

Sinnbeck's avatar

Why not put all project specific routes in a route group that adds the id to the url. Then it's forced

Route::prefix('editor/{project}')->group(function () {
    //your routes 
});
1 like
rhand's avatar
Level 6

@Sinnbeck I used

...
Route::prefix('{project}')->middleware('auth', 'subcheck')->group(function () {
        Route::get('get-pages', [Editor\ProjectController::class, 'getPages']);
    });
...

with

...
// Get Pages
    axios.get(`/editor/get-pages`)
        .then(res => {
            this.$store.dispatch('editor/setPages', res.data.data)
        })
        .catch(() => {});
},
...

and the route does load. But I can still change the project id site/editor?project=22 to open a project that does not belong to the current user. So this may work but then my middleware does not prevent this.. Perhaps because it is authentication and not authorization.. But app/Providers/AuthServiceProvider.php does load in and Project policy

class ProjectPolicy
{
    use HandlesAuthorization;

    public function edit(User $user, Project $project)
    {
        return $user->id === $project->user_id;
    }

    public function update(User $user, Project $project)
    {
        return $user->id === $project->user_id;
    }
}

should also work should it not? Then again.. this checks for edit and update, not for direct loading as I do now I guess..

Sinnbeck's avatar

@rhand Uhm you have /{project}/get-pages as the url but you are telling laravel that the project id is editor?

// Get Pages
    axios.get(`/12/get-pages`) //setting the id
        .then(res => {
            this.$store.dispatch('editor/setPages', res.data.data)
        })
        .catch(() => {});
},
1 like
rhand's avatar
Level 6

@Sinnbeck In the end we were able to keep the routes as it as the get requests. We added this to the _construct(): in the controllers dealing with projects:

...
use Illuminate\Support\Facades\Auth;
...
$this->currentProject = Auth::user()->projects()->findOrFail(request()->project);
...
Snapey's avatar

@rhand authorisation is not just about what actions a user can perform, but also, with which resources.

You can use policies to check ownership of the project so that it is safe to specify in the URL

1 like
rhand's avatar
Level 6

@Snapey True that we only check whether a user can edit or update a project in the Project policy now. How would adding resource check method or separate policy look like? The Project Controller is no resource controller but a general one dealing with updates, loading and such.

Please or to participate in this conversation.