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
?
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).
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!