52531dd41a
Auth was effectively broken for every login path: the Telegram handler built a Set-Cookie header but never attached it to the response, and Ghost Protocol (seed) login never set a cookie at all — so billing, the game, and admin actions never actually worked once the frontend was fixed to stop trying to read an HttpOnly cookie from JS. Replaced the bare 10-minute JWT with AuthService::issue_session: a 15-minute access cookie plus a rotating 30-day refresh cookie (opaque token, only its SHA-256 hash stored in the new refresh_sessions table); reusing an already- rotated refresh token now revokes every session for that user. CSRF/CORS origin allowlists moved from a single hardcoded domain to env-configured lists so the same session works across the landing and account subdomains. The bot's WebApp deep links (Личный Кабинет/Тарифы/Синдикат) now bridge through a short-lived bootstrap token exchanged via POST /auth/exchange instead of a bare access token in the URL. Nodes: /nodes/routing now serializes the tunnel_* fields, public_key, sni_domain and a one-time session token gated on an active subscription instead of a stub ip/port/protocol list; node provisioning returns 202 immediately and finishes in the background instead of blocking the request for the whole SSH run; a new background task TCP-pings nodes every 60s and flips online/offline itself; admin can now force-restart a node over SSH. Billing: TON exchange rate is cached in Redis (60s) instead of hitting TonAPI on every invoice, and the request/response now actually uses the requested currency and TonAPI's real (uppercase) key casing — previously only USD/RUB were ever requested and the lookup used the wrong case, so non-USD/RUB plans could never price correctly. Cyberhack: the server now generates and stores the board itself and replays the client's raw click path to compute the score, instead of clamping a client-reported number — closes the "final score is whatever the browser sends" hole flagged in the security audit. Frontend: api.ts no longer tries to read the HttpOnly session cookie from JS (that never worked) and instead relies on same-origin credentials plus a silent refresh-and-retry on 401; AdminDashboard's restart/delete actions are wired up; Auth.tsx drops the artificial delays and requires an explicit seed download/confirmation before continuing, since a lost Ghost seed is unrecoverable. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
30 lines
1.7 KiB
SQL
30 lines
1.7 KiB
SQL
-- =====================================================================
|
|
-- Repurpose неиспользуемого черновика Magic Link (auth_sessions) под
|
|
-- реальный флоу: ротируемые refresh-токены для веб-сессий. Раньше access-JWT
|
|
-- жил 10 минут без какого-либо обновления, и юзера тихо разлогинивало каждые
|
|
-- 10 минут — см. README/SECURITY_AUDIT.md. Храним только SHA-256 хеш
|
|
-- предъявляемого токена, никогда сырое значение.
|
|
-- =====================================================================
|
|
DROP TABLE IF EXISTS auth_sessions;
|
|
|
|
CREATE TABLE refresh_sessions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
token_hash VARCHAR(64) NOT NULL UNIQUE, -- hex(sha256(refresh_token))
|
|
user_agent VARCHAR(255),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
revoked_at TIMESTAMPTZ
|
|
);
|
|
|
|
CREATE INDEX idx_refresh_sessions_user ON refresh_sessions(user_id);
|
|
|
|
-- =====================================================================
|
|
-- Поля, недостающие для полноценного TunnelConfig в GET /nodes/routing:
|
|
-- статический публичный ключ ноды (для клиентского пиннинга) и домен-декой
|
|
-- для ClientHello (см. src/modules/nodes/controller.rs::get_node_routing).
|
|
-- =====================================================================
|
|
ALTER TABLE vpn_nodes
|
|
ADD COLUMN public_key TEXT,
|
|
ADD COLUMN sni_domain VARCHAR(255);
|