Installation:
composer require dneustadt/csrf-cookie-bundle
Enable the bundle in config/bundles.php:
return [
// ...
Dneustadt\CsrfCookieBundle\DneustadtCsrfCookieBundle::class => ['all' => true],
];
Basic Configuration:
Override default settings in config/packages/dneustadt_csrf_cookie.yaml:
dneustadt_csrf_cookie:
enable: true
name: XSRF-TOKEN # Cookie name (must match client-side expectations)
First Use Case:
axios.get('/csrf-token'); // Triggers cookie generation
axios.post('/api/endpoint', { data: '...' }, {
headers: { 'X-XSRF-TOKEN': document.cookie.match(/XSRF-TOKEN=([^;]+)/)[1] }
});
csrf_token:
# config/routes.yaml
api_csrf_token:
path: /csrf-token
methods: [GET]
defaults: { _controller: 'dneustadt_csrf_cookie.controller.csrf_token' }
Token Generation:
/csrf-token route (or custom path via config) to fetch the token cookie.HttpFoundation\Cookie to manually set cookies if needed:
$response = new Response();
$response->headers->setCookie(new Cookie('XSRF-TOKEN', $token, [
'expires' => time() + 3600,
'path' => '/',
'secure' => true, // HTTPS only
]));
Client-Side Handling:
X-XSRF-TOKEN header if cookie exists.const token = document.cookie.split('; ').find(row => row.startsWith('XSRF-TOKEN='))?.split('=')[1];
fetch('/api/endpoint', { headers: { 'X-XSRF-TOKEN': token } });
Symfony Controller:
CSRF_TOKEN is validated via Symfony’s built-in validator:
#[Route('/api/endpoint', methods: ['POST'])]
public function submit(Request $request): Response
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$this->validateCsrfToken('csrf', $request->headers->get('X-XSRF-TOKEN'));
// ...
}
Custom Routes:
dneustadt_csrf_cookie:
routes:
- path: /api/csrf
methods: [GET]
$client->getContainer()->get('request_stack')->getCurrentRequest()->headers->set('X-XSRF-TOKEN', 'valid_token');
Access-Control-Expose-Headers includes X-XSRF-TOKEN in preflight responses.Cookie Scope:
secure: true requires HTTPS; test locally with secure: false or trusted_proxies config.domain must match your site’s domain (e.g., .example.com for subdomains).Token Validation:
CSRF_TOKEN validator expects the token name to match config (name key).framework:
csrf_token_generator:
token_param: _csrf_token
Axios Quirks:
withCredentials: true for cookies:
axios.get('/csrf-token', { withCredentials: true });
Double Submissions:
X-XSRF-TOKEN and _csrf_token (Symfony’s default)./csrf-token route is hit (check Symfony logs).Application > Cookies) to confirm cookie presence.error_log($request->headers->get('X-XSRF-TOKEN'));
CsrfTokenManager).CsrfTokenManager to use a database or cache:
$tokenManager = new CustomTokenManager($container->get('security.csrf.token_manager'));
$container->set('security.csrf.token_manager', $tokenManager);
CsrfTokenController to add logic (e.g., role-based token generation):
public function csrfTokenAction(Request $request): Response
{
if (!$this->isGranted('ROLE_API_USER')) {
throw $this->createAccessDeniedException();
}
return parent::csrfTokenAction($request);
}
$response = new Response();
$response->headers->setCookie(new Cookie('XSRF-TOKEN', $newToken, ['expires' => time() + 300]));
How can I help you explore Laravel packages today?