phplaravelcookiescsrflaravel-sanctum

Laravel on sub domain ignore parent cookies when


I have a laravel app on a parent domain. SESSION_DOMAIN is set to .domain.com because I want the cookies to be shared with another subdomain first.domain.com.

I now want to publish another app at second.domain.com which is separate from domain.com and first.domain.com.

The SESSION_DOMAIN for this second app is correctly set to second.domain.com.

Everytime I try to login via sanctum I get an error CSRF token mismatch.

I'm pretty certain this is due to the multiple cookies on multiple domains since if I clear my cookies for the parent domain.com or use incognito mode or another browser then the issue goes away.

The issue only exists when both .domain.com and second.domain.com have a XSRF-TOKEN cookie token set.

Everything else seems to work fine, presumably because the other cookie Laravel uses called _session is prefixed by the app name and therefore unique.

Where as the XSRF-TOKEN is always called XSRF-TOKEN.

How can I set it up so that my parent domain can share cookies with first.domain.com but doesn't interfere with second.domain.com?


Solution

  • I figured out a work around that seems to work.

    By adding the following to my blade template that is rendering the SPA.

    <meta name="csrf-token" content="{{ csrf_token() }}">
    

    And then when initialising Axios (this is in bootstrap.js in the default Laravel setup) add the crsf-token as a default header.

    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
    

    This bypasses the cookie, you can see in Laravel's VerifyCsrfToken that it checks for this header first (actually second you could also send _token as a GET or POST param).

    https://github.com/laravel/framework/blob/4ffac6f71467562dbe670f893af787bf5c610103/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php#L151

    The one gotcha i've found is that when you logout of Sanctum you usually want to reset the token.

    The best bet would be to return csrf_token() in the Logout response and then update the header similar to above.

    e.g:

    /**
         * @param \Illuminate\Http\Request $request
         * @return \Illuminate\Http\JsonResponse
         */
        public function logout(Request $request): JsonResponse
        {
            Auth::guard('web')->logout();
            $request->session()->invalidate();
            $request->session()->regenerateToken();
            return response()->json([
                'message' => 'You have been logged out.',
                'csrf_token' => csrf_token()
            ]);
        }
    
    axios.post('/api/auth/logout').then(response => {
        window.axios.defaults.headers.common['X-CSRF-TOKEN'] = response.data.csrf_token
    })
    

    I hope this helps someone else with the same problem!