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

rross128's avatar

Axios post and session persistence with Laravel API

I'm pretty sure I'm doing something wrong here, so I'd be grateful to whoever can help.

So I'm working on a Laravel 7.0 site with a shopping cart, and I'm trying to cut down on load screens using some Vue components. When the user adds something to their cart, I send the info to the back end with an API request using Axios to save the info in the DB for future reference. If a quote number doesn't exist, the app creates it and sends it back. If the user was logged in, I could easily see if they have any open carts and attach it that way, but I need to ensure that I can save the cart for guest users as well. The problem I'm having is persisting a quote number in the session when a guest user goes to another page or even refreshes the current page.

I saw some threads with a suggestion about creating a middleware using the StartSession object and adding it to the route.

'session' => [
     \Illuminate\Session\Middleware\StartSession::class
]

I saw in the request using Telescope that the session variable was set in the response, but it still didn't persist on the next page load.

Another forum post suggested adding withCredentials to Axios to help persist items, but that didn't do anything.

axios.defaults.withCredentials = true;

I'm guessing the issue is a disconnect in the sessions between the browser and Axios. I've managed a workaround by adding a hidden iframe with a form in it. When Axios returns a quote number, I use the hidden form to resubmit the quote number with the browser to persist the variable in the session. While it works, I fear this could be easily exploited.

What's the right way to do this?

0 likes
6 replies
squibby's avatar
squibby
Best Answer
Level 8

Is this just to keep track of whats in a shopping cart? Why not just use the browser local storage to store the ids of any products etc and quantities selected. Then only persist the data to backend when someone actually completes the transaction.

This way even if a guest user comes back a day later when they open the page you can show the cart is full from what is in the local storage.

rross128's avatar

That's for the suggestion.

The site is for an insurance company, so it's a little more complicated than Product#/Quantity type of data needed to recreate the cart, but I'll look into browser local storage to see if there's a way I can use it for what I need.

rross128's avatar

I used the browser storage to store the quote number, then used a get request to look up the quote. The request isn't that large so it still processes fairly quickly, and I still have some control in the backend to invalidate an expired quote. Thanks for the suggestion.

rkaalma's avatar

I'm facing a similar situation as OP - a custom authentication flow where two axios requests are made:

  1. Async POST request to generate a control code that's displayed in front-end (for the user to authenticate trough a 3rd party app - similar to 2FA logic) and also a secure hash that should be stored in encrypted session cookie for for next request;
  2. Async POST request to ping the 3rd party app for successful user interaction - where the secure hash from session cookie should be available;

I can't figure out how to get the access the session cookie initially generated since the session seems to regenerate at some point - any ideas?

Laravel 9 (SESSION_DRIVER= cookie) + Sanctum + Fortify (for email/password login - not related) + Vue + Nuxt 2

1 like
rross128's avatar

I wasn't able to find a session-based solution for my problem. What I ended up doing was storing a reference ID in the local browser using the Javascript local storage driver, and then querying the data through an Axios post in my Vue components. My backend route has auth and a policy guard to prevent undesired access.

You might be able to find a better solution with Sanctum. I know it has some specifics around the cookie domain that can cause issues similar to your description.

//.env file
'domain' => '.domain.com',

https://laracasts.com/series/whats-new-in-laravel-7/episodes/6

rkaalma's avatar

At some point all the different configuration combos got mixed up and overwhelming but it seems "I tamed the horse". So what I got..

Let's get started

Overall

  • This example is based on local environment, replicating production domain structure
  • Example is based on Lando, changing every example.lndo.site to your TLD should work
  • Both Laravel API and NUXT front-end are on a subdomain of the main TLD
  • Laravel API is served locally with Lando, accessable directly from http://api.example.lndo.site
  • SPA (nuxtjs) is server locally with Lando, accessable directly from http://admin.example.lndo.site
  • Using Laravel Sanctum for CSRF handling (/sanctum/csrf-token)
  • Using Laravel Fortify (with it's provided routes) for main login routes
  • Using custom controller for "two-part" (2FA logic) login - that must support cookie vars throughout multiple requests
  • Don't forget to php artisan route:cache after doing changes to Laravel routes - I must have burned ~16 hours of debugging last week for not doing it (knowing it now)...

Laravel

Important configuration

// .env

APP_URL='https://api.example.lndo.site'
FRONTEND_URL='https://admin.example.lndo.site'
SANCTUM_STATEFUL_DOMAINS='*.example.lndo.site'
SESSION_DRIVER=cookie
SESSION_DOMAIN='.example.lndo.site'
SESSION_SECURE_COOKIE=true
// config/cors.php

 'paths' => ['api/*', 'auth/*', 'sanctum/csrf-cookie'],
 'allowed_methods' => ['*'],
 'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')],
 'allowed_origins_patterns' => [],
 'allowed_headers' => ['*'],
 'exposed_headers' => [],
 'max_age' => 0,
 'supports_credentials' => true,
// config/fortify.php

'prefix' => 'auth',
'views' => false,
// config/session.php

'encrypt' => true,
'http_only' => true,
'same_site' => 'lax',

Routes

I created a separate middleware group although it's very similar to web middleware group:

// app/Http/Kernel.php

protected $middlewareGroups = [
    // added custom route group
    'mw-group-name' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
    ]
]
// app/Providers/RouteServiceProvider.php

public function boot()
{
        // added custom routes
        Route::middleware('mw-group-name')
            ->prefix('auth')
            ->group(base_path('routes/auth.php'));
    });
}
// app/routes/auth.php

// Here id added the routes that needed to have session storage
// while not authenticated that "stays alive" for multiple requests

Writing to session

// app/Http/Controllers/SomeController.php

// On the first request/function I put variables to session and SAVE
$request->session()->put('some_var_value_1', $someVarValue1);
$request->session()->put('some_var_value_2', $someVarValue2);
$request->session()->save();

// On the next request/function I get the variables from session
$request->session()->get('some_var_value_1');
$request->session()->get('some_var_value_2');

SPA (nuxt) conf

// nuxt.config.js

axios: {
  baseUrl: process.env.API_URL || 'http://api.example.lndo.site',
  credentials: true,
  headers: {
    accept: 'application/json',
  },
},

auth: {
  redirects: {
    login: '/login',
    logout: '/login',
    home: '/',
  },
  strategies: {
    cookie: {
      endpoints: {
        csrf: {
          url: '/sanctum/csrf-cookie',
        },
        login: {
          url: '/auth/login',
        },
        logout: {
          url: '/auth/logout',
        },
        register: {
          url: '/auth/register',
        },
        user: {
          url: '/api/me',
        },
      },
    },
  },
},

SPA (nuxtjs) request flow

// components/SomeComponent.vue

<script>
export default {
  methods: {
    async doRequest() {
      await this.$axios.get('/sanctum/csrf-cookie')
      await this.$axios
        .post('/auth/custom-start', {
          some_var: this.someUserInput,
        })
        // Here I'm putting some vars to cookie for later use
        .then((response) => {
          // do front-end stuff
        })

      await this.$axios.get('/sanctum/csrf-cookie')
      await this.$axios
        .post('/auth/custom-process')
        // Here I'm using cookie vars in the back-end controller, sent with
        //request as a encrypted cookie
        .then((response) => {
          // do front-end stuff
        })
    },
  },
}
</script>

The end.

I really hope it helps some future troubleshooter save some time. Feel free to ask more specifics. Cheers.

2 likes

Please or to participate in this conversation.