fix: CSRF regression — grace period for old sessions, GET check-port/refresh-ip, Peers.jsx native fetch tokens

- check_csrf() now issues a token for sessions that predate CSRF (existing logins) instead of blocking them
- /api/wireguard/check-port and /api/wireguard/refresh-ip accept GET so native fetch calls bypass the token requirement
- WireGuard.jsx: changed three native fetch POST → GET for the above endpoints
- Peers.jsx: add X-CSRF-Token header to three native fetch mutation calls (calendar collection, peer PUT, clear-reinstall)
- api.js: export getCsrfToken() so non-Axios callers can read the current token

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-27 12:18:02 -04:00
parent a43f9fbf0d
commit 9aaacd11cc
4 changed files with 28 additions and 10 deletions
+12 -4
View File
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { Plus, Trash2, Edit, Eye, Shield, Copy, Download, Key, AlertTriangle, CheckCircle, Globe, Lock, Users, Server } from 'lucide-react';
import { peerRegistryAPI, wireguardAPI } from '../services/api';
import { peerRegistryAPI, wireguardAPI, getCsrfToken } from '../services/api';
import { useConfig } from '../contexts/ConfigContext';
import QRCode from 'qrcode';
@@ -194,7 +194,11 @@ PersistentKeepalive = ${peer.persistent_keepalive || 25}`;
if (formData.create_calendar) {
try {
await fetch(`/api/calendar/create-user-collection?user=${formData.name}`, { method: 'POST', credentials: 'include' });
await fetch(`/api/calendar/create-user-collection?user=${formData.name}`, {
method: 'POST',
credentials: 'include',
headers: { 'X-CSRF-Token': getCsrfToken() || '' },
});
} catch {}
}
@@ -225,7 +229,7 @@ PersistentKeepalive = ${peer.persistent_keepalive || 25}`;
const r = await fetch(`/api/peers/${selectedPeer.name}`, {
method: 'PUT',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': getCsrfToken() || '' },
body: JSON.stringify({
description: formData.description,
internet_access: formData.internet_access,
@@ -292,7 +296,11 @@ PersistentKeepalive = ${peer.persistent_keepalive || 25}`;
const handleConfigDownloaded = async (peerName) => {
try {
await fetch(`/api/peers/${peerName}/clear-reinstall`, { method: 'POST', credentials: 'include' });
await fetch(`/api/peers/${peerName}/clear-reinstall`, {
method: 'POST',
credentials: 'include',
headers: { 'X-CSRF-Token': getCsrfToken() || '' },
});
setPeers(ps => ps.map(p => p.name === peerName ? { ...p, config_needs_reinstall: false } : p));
} catch {}
};
+3 -3
View File
@@ -29,11 +29,11 @@ function WireGuard() {
setIsRefreshingIp(true);
try {
// Refresh IP first (fast)
const ipResp = await fetch('/api/wireguard/refresh-ip', { method: 'POST', credentials: 'include' });
const ipResp = await fetch('/api/wireguard/refresh-ip', { credentials: 'include' });
const ipData = await ipResp.json();
setServerConfig(prev => ({ ...prev, ...ipData, port_open: 'checking' }));
// Then check port (slow — external call)
const portResp = await fetch('/api/wireguard/check-port', { method: 'POST', credentials: 'include' });
const portResp = await fetch('/api/wireguard/check-port', { credentials: 'include' });
const portData = await portResp.json();
setServerConfig(prev => ({ ...prev, port_open: portData.port_open }));
} catch (e) {
@@ -56,7 +56,7 @@ function WireGuard() {
if (serverConfigResponse) {
setServerConfig({ ...serverConfigResponse, port_open: 'checking' });
// Check port asynchronously so page loads fast
fetch('/api/wireguard/check-port', { method: 'POST', credentials: 'include' })
fetch('/api/wireguard/check-port', { credentials: 'include' })
.then(r => r.json())
.then(d => setServerConfig(prev => ({ ...prev, port_open: d.port_open ?? false })))
.catch(() => setServerConfig(prev => ({ ...prev, port_open: false })));