fix csrf mismatch
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { resolvePaddleLocale } from '../steps/PaymentStep';
|
||||
import { resolveCheckoutCsrfToken, resolvePaddleLocale } from '../steps/PaymentStep';
|
||||
|
||||
describe('resolvePaddleLocale', () => {
|
||||
it('returns short locale when given region-specific tag', () => {
|
||||
@@ -18,3 +18,27 @@ describe('resolvePaddleLocale', () => {
|
||||
expect(resolvePaddleLocale('es')).toBe('es');
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveCheckoutCsrfToken', () => {
|
||||
beforeEach(() => {
|
||||
document.head.innerHTML = '';
|
||||
document.cookie = 'XSRF-TOKEN=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
|
||||
});
|
||||
|
||||
it('prefers the meta csrf token when present', () => {
|
||||
const meta = document.createElement('meta');
|
||||
meta.setAttribute('name', 'csrf-token');
|
||||
meta.setAttribute('content', 'meta-token');
|
||||
document.head.appendChild(meta);
|
||||
|
||||
document.cookie = 'XSRF-TOKEN=cookie-token';
|
||||
|
||||
expect(resolveCheckoutCsrfToken()).toBe('meta-token');
|
||||
});
|
||||
|
||||
it('falls back to the XSRF-TOKEN cookie when meta is missing', () => {
|
||||
document.cookie = 'XSRF-TOKEN=cookie-token';
|
||||
|
||||
expect(resolveCheckoutCsrfToken()).toBe('cookie-token');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,6 +36,45 @@ const PADDLE_SCRIPT_URL = 'https://cdn.paddle.com/paddle/v2/paddle.js';
|
||||
const PADDLE_SUPPORTED_LOCALES = ['en', 'de', 'fr', 'es', 'it', 'nl', 'pt', 'sv', 'da', 'fi', 'no'];
|
||||
const PRIMARY_CTA_STYLES = 'min-w-[200px] disabled:bg-muted disabled:text-muted-foreground disabled:hover:bg-muted disabled:hover:text-muted-foreground';
|
||||
|
||||
const getCookieValue = (name: string): string | null => {
|
||||
if (typeof document === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
|
||||
|
||||
return match ? decodeURIComponent(match[1]) : null;
|
||||
};
|
||||
|
||||
export function resolveCheckoutCsrfToken(): string {
|
||||
if (typeof document === 'undefined') {
|
||||
return '';
|
||||
}
|
||||
|
||||
const metaToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
||||
|
||||
if (metaToken && metaToken.length > 0) {
|
||||
return metaToken;
|
||||
}
|
||||
|
||||
return getCookieValue('XSRF-TOKEN') ?? '';
|
||||
}
|
||||
|
||||
function buildCheckoutHeaders(): HeadersInit {
|
||||
const csrfToken = resolveCheckoutCsrfToken();
|
||||
const headers: HeadersInit = {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
};
|
||||
|
||||
if (csrfToken) {
|
||||
headers['X-CSRF-TOKEN'] = csrfToken;
|
||||
headers['X-XSRF-TOKEN'] = csrfToken;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
export function resolvePaddleLocale(rawLocale?: string | null): string {
|
||||
if (!rawLocale) {
|
||||
return 'en';
|
||||
@@ -271,14 +310,9 @@ export const PaymentStep: React.FC = () => {
|
||||
setAwaitingConfirmation(false);
|
||||
|
||||
try {
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
|
||||
const response = await fetch('/checkout/free-activate', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
'X-CSRF-TOKEN': csrfToken,
|
||||
},
|
||||
headers: buildCheckoutHeaders(),
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
package_id: selectedPackage.id,
|
||||
@@ -349,11 +383,8 @@ export const PaymentStep: React.FC = () => {
|
||||
|
||||
const response = await fetch('/paddle/create-checkout', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '',
|
||||
},
|
||||
headers: buildCheckoutHeaders(),
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
package_id: selectedPackage.id,
|
||||
locale: paddleLocale,
|
||||
|
||||
Reference in New Issue
Block a user