Skip to content

Commit 2c0d32f

Browse files
hlecorchenicolas-grekas
authored andcommitted
[stimulus-bundle] Export CSRF protection helpers
1 parent 84862d3 commit 2c0d32f

File tree

1 file changed

+40
-22
lines changed

1 file changed

+40
-22
lines changed
Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,79 @@
1-
var nameCheck = /^[-_a-zA-Z0-9]{4,22}$/;
2-
var tokenCheck = /^[-_/+a-zA-Z0-9]{24,}$/;
1+
const nameCheck = /^[-_a-zA-Z0-9]{4,22}$/;
2+
const tokenCheck = /^[-_\/+a-zA-Z0-9]{24,}$/;
33

44
// Generate and double-submit a CSRF token in a form field and a cookie, as defined by Symfony's SameOriginCsrfTokenManager
55
document.addEventListener('submit', function (event) {
6-
var csrfField = event.target.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
6+
generateCsrfToken(event.target);
7+
}, true);
8+
9+
// When @hotwired/turbo handles form submissions, send the CSRF token in a header in addition to a cookie
10+
// The `framework.csrf_protection.check_header` config option needs to be enabled for the header to be checked
11+
document.addEventListener('turbo:submit-start', function (event) {
12+
const h = generateCsrfHeaders(event.detail.formSubmission);
13+
Object.keys(h).map(function (k) {
14+
event.detail.formSubmission.fetchRequest.headers[k] = h[k];
15+
});
16+
});
17+
18+
// When @hotwired/turbo handles form submissions, remove the CSRF cookie once a form has been submitted
19+
document.addEventListener('turbo:submit-end', function (event) {
20+
removeCsrfToken(event.detail.formSubmission.formElement);
21+
});
22+
23+
export function generateCsrfToken (formElement) {
24+
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
725

826
if (!csrfField) {
927
return;
1028
}
1129

12-
var csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
13-
var csrfToken = csrfField.value;
30+
let csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
31+
let csrfToken = csrfField.value;
1432

1533
if (!csrfCookie && nameCheck.test(csrfToken)) {
1634
csrfField.setAttribute('data-csrf-protection-cookie-value', csrfCookie = csrfToken);
1735
csrfField.defaultValue = csrfToken = btoa(String.fromCharCode.apply(null, (window.crypto || window.msCrypto).getRandomValues(new Uint8Array(18))));
18-
csrfField.dispatchEvent(new Event('change', {bubbles: true}));
36+
csrfField.dispatchEvent(new Event('change', { bubbles: true }));
1937
}
2038

2139
if (csrfCookie && tokenCheck.test(csrfToken)) {
22-
var cookie = csrfCookie + '_' + csrfToken + '=' + csrfCookie + '; path=/; samesite=strict';
40+
const cookie = csrfCookie + '_' + csrfToken + '=' + csrfCookie + '; path=/; samesite=strict';
2341
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie;
2442
}
25-
});
43+
}
2644

27-
// When @hotwired/turbo handles form submissions, send the CSRF token in a header in addition to a cookie
28-
// The `framework.csrf_protection.check_header` config option needs to be enabled for the header to be checked
29-
document.addEventListener('turbo:submit-start', function (event) {
30-
var csrfField = event.detail.formSubmission.formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
45+
export function generateCsrfHeaders (formElement) {
46+
const headers = {};
47+
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
3148

3249
if (!csrfField) {
33-
return;
50+
return headers;
3451
}
3552

36-
var csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
53+
const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
3754

3855
if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) {
39-
event.detail.formSubmission.fetchRequest.headers[csrfCookie] = csrfField.value;
56+
headers[csrfCookie] = csrfField.value;
4057
}
41-
});
4258

43-
// When @hotwired/turbo handles form submissions, remove the CSRF cookie once a form has been submitted
44-
document.addEventListener('turbo:submit-end', function (event) {
45-
var csrfField = event.detail.formSubmission.formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
59+
return headers;
60+
}
61+
62+
export function removeCsrfToken (formElement) {
63+
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
4664

4765
if (!csrfField) {
4866
return;
4967
}
5068

51-
var csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
69+
const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
5270

5371
if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) {
54-
var cookie = csrfCookie + '_' + csrfField.value + '=0; path=/; samesite=strict; max-age=0';
72+
const cookie = csrfCookie + '_' + csrfField.value + '=0; path=/; samesite=strict; max-age=0';
5573

5674
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie;
5775
}
58-
});
76+
}
5977

6078
/* stimulusFetch: 'lazy' */
6179
export default 'csrf-protection-controller';

0 commit comments

Comments
 (0)