Open-source web implementation of Steam Desktop Authenticator (SDA) with multi-user and multi-account support.
Russian version: readme_ru.md
- Backend: Fastify + TypeScript (REST API + WebSocket notifications)
- Frontend: React + Vite + Tailwind + PWA
- DB: MySQL 8 in Docker with encrypted secrets/session data
- Shared infra: Redis for rate limiting in Docker deployments
- Bot: Aiogram (single Telegram bot for all users)
- License: MIT
- Fastify: lower overhead and better throughput for polling/real-time workloads.
- TypeScript: safer refactoring and shared API contracts.
- MySQL 8 + Docker internal network: relational consistency, easy VPS deploy, DB isolated from public access.
- Redis-backed rate limiting: shared throttling across backend instances and restarts, with in-memory fallback outside Redis setups.
- React + Vite + Tailwind: fast DX, responsive UI, simple theming.
- Vite PWA plugin: installable app + service worker support.
- Aiogram bot: mature async Telegram framework for command and deep-link flows.
.maFileimport/export with AES-256-GCM encryption in DB.- Multi-user + unlimited Steam accounts per user.
- Steam code generation from
shared_secret. - Trade/login confirmations queue with manual confirm/reject.
- Auto-confirm toggles with per-account delay.
- Account folders and tags with filtering in the accounts view.
- Telegram OAuth-like login via bot deep-link.
- Telegram account linking with
/add=<code>. - Telegram commands:
/accounts,/codes,/confirm <trade_id>,/status. - Passkey/WebAuthn login, including usernameless passkey login when the device supports discoverable credentials.
- TOTP authenticator app support.
- Recovery codes for break-glass account recovery.
- JWT cookie sessions, CSRF protection, Helmet, rate limiting, optional Turnstile.
- Registration policies: open, disabled, domain allowlist, invite-only.
- Admin panel with registration controls, invite codes, user deletion, and audit trail.
- Webhook notifications for login/trade/session-expired events with generic and Discord targets.
- i18n EN/RU + light/dark theme.
- OpenAPI JSON at
/api-docs/openapi.jsonwhenOPENAPI_ENABLED=true.
- Email:
admin@admin.com - Password:
admin123
Override with .env: ADMIN_EMAIL, ADMIN_PASSWORD
Important:
- The default admin password is for local bootstrap only.
- Production startup is blocked if
ADMIN_PASSWORD=admin123. - Production startup is also blocked if
JWT_SECRET,COOKIE_SECRET, orENCRYPTION_KEYstill use placeholder-style values such aschange_me..., are shorter than 32 characters, or reuse the same secret value.
- Monolith API-first backend (
/api/...) + WS (/ws) on port3001. - Frontend SPA on port
3000. - Frontend server (Vite/Nginx) proxies
/apiand/wsto backend. - MySQL and Redis are isolated in internal Docker network and not exposed externally.
- Optional reverse proxy service (
nginx) in compose profileproxy.
.
├── backend/ # Fastify API (TypeScript)
├── frontend/ # React + Vite + Tailwind + PWA
├── bot/ # Aiogram Telegram bot
├── docker/
│ ├── mysql/init.sql # Schema bootstrap
│ └── nginx/nginx.conf # Reverse proxy example
├── docker-compose.yml
├── docker-compose.dev.yml
├── Makefile
├── .env.example
├── README.md
└── readme_ru.md
cp .env.example .env
make devOpen:
- Frontend:
http://localhost:3000 - Backend API:
http://localhost:3001 - OpenAPI:
http://localhost:3001/api-docs/openapi.json(enabled by default in dev/test)
make dev- Docker dev stack with hot reload.make build- build production images.make up- run production stack detached.make deploy- alias formake up.make down- stop and remove containers.make lint- backend + frontend lint.make test- backend Jest + frontend Cypress (via docker service).make logs- follow container logs.
See .env.example.
Core variables:
DB_*MySQL connection and bootstrap user credentials.JWT_SECRET,COOKIE_SECRET,ENCRYPTION_KEYsecurity secrets.APP_URL,API_URLbrowser/backend origins.RATE_LIMIT_REDIS_URL,RATE_LIMIT_REDIS_PREFIXshared rate limiting backend; Docker Compose defaults to internal Redis and aNODE_ENV-based prefix.FORCE_HTTPSenables HTTP->HTTPS redirects in production (trueby default, can be disabled for special deployments).OPENAPI_ENABLEDcontrols/api-docs/openapi.json; enabled by default in dev/test and should stay off in production unless explicitly needed.TELEGRAM_BOT_TOKEN,TELEGRAM_BOT_USERNAMEbot settings.STEAM_POLL_INTERVAL_SECauto-confirm polling interval.TURNSTILE_ENABLED,TURNSTILE_SITE_KEY,TURNSTILE_SECRET_KEYoptional Cloudflare Turnstile backend protection.VITE_TURNSTILE_SITE_KEYfrontend public site key for invisible Turnstile registration flow.
Generate strong secrets before production deploy, for example:
openssl rand -hex 32
openssl rand -hex 32
openssl rand -hex 32Then place the generated values into .env as:
JWT_SECRET=<first generated value>
COOKIE_SECRET=<second generated value>
ENCRYPTION_KEY=<third generated value>Important operator notes:
- If
TELEGRAM_BOT_TOKENis empty or starts withchange_me, bot service stays in disabled idle mode. - If you enable
OPENAPI_ENABLEDin production, prefer exposing it only behind admin auth, VPN, or IP allowlisting. APP_URLmust exactly match the browser-facing origin used for WebAuthn/passkeys. If your reverse proxy exposes another hostname or scheme, passkey login/registration will fail.
- MA encryption: AES-256-GCM per user.
- Key derivation: per-user key derived from bcrypt password hash + global
ENCRYPTION_KEY. - Auth: JWT in HTTP-only cookie.
- 2FA: Telegram, passkeys, TOTP authenticator app.
- Break-glass recovery: one-time recovery codes can be generated from settings; using one resets configured 2FA and removes registered passkeys for that user.
- Sensitive actions: password re-confirmation required before
.maFileexport, recovery code reveal, manual Steam session updates, and recovery code regeneration. - CSRF: double-submit protection for mutating endpoints.
- Brute-force/DoS:
rate-limiter-flexiblein auth/write paths. - Shared throttling: Docker deployments use Redis-backed rate limits with in-memory insurance fallback if Redis is temporarily unavailable; current Compose defaults do not persist limiter state across Redis restarts.
- Anti-bot registration: signed registration challenge, honeypot, dedicated registration rate limiter and optional invisible Cloudflare Turnstile.
- Hardening:
helmet, CORS with credentials. - WebSocket auth: cookie/bearer only, query-string auth disabled.
- API caching:
/api/*responses are served withCache-Control: no-store. - DB isolation: MySQL only on internal Docker network (
db_internal).
The admin panel supports these registration modes:
open- normal self-registration.disabled- registration blocked for everyone.domain_allowlist- only email domains from the configured allowlist can register.invite_only- registration requires a valid invite code.
Invite-only mode also includes invite code management in the admin panel.
- In Settings click
Generate /add code. - Send
/add=<code>to the bot within 15 minutes. - The bot binds
telegram_user_idto your web user.
- On login page click
Login via Telegram. - Open the bot manually using the provided button or
/start login_<code>command. - Confirm the login in the bot.
- Return to the web page; it polls and creates the session automatically.
Deploy frontend and backend together when changing Telegram login polling, because the flow now requires the x-telegram-poll-token header instead of the legacy query-string token.
- Passkeys can be registered from Settings.
- Login supports both email-first passkey flow and usernameless passkey login.
- Usernameless passkey login depends on discoverable credentials supported by the authenticator/platform.
- TOTP setup is started in Settings.
- The app shows a QR code and manual secret.
- After verifying the first code, TOTP can be selected as the primary 2FA method.
- Recovery codes are generated from Settings after password re-authentication.
- Codes are shown only once.
- Using a recovery code signs the user in and clears configured 2FA/passkeys so the user can recover access safely.
- Accounts can be grouped into folders.
- Accounts can have multiple tags.
- The accounts page supports filtering by folder and tag.
- Account details page supports assigning folder/tags per account.
The admin panel includes an audit view with filters by category and actor.
Main categories:
steamauthsecurity
Examples of tracked events:
- registration policy changes
- invite creation/deletion
- webhook creation/testing/deletion
- TOTP enablement
- recovery code regeneration/use
- admin user deletion
- Steam confirmations and session events
Webhook settings are available in the user Settings page.
Supported targets:
generic- JSON POST webhookdiscord- Discord embed webhook
Supported events:
tradeloginsteam_session_expired
Notes:
- Webhook delivery failures are recorded in DB and do not break the in-app notification flow.
- Test delivery is available from Settings.
Generic webhook payload shape:
{
"event": "trade",
"occurredAt": "2026-03-22T12:00:00.000Z",
"payload": {
"accountId": 1,
"accountAlias": "Main",
"headline": "New trade offer",
"summary": "..."
}
}Steam mobile confirmations require valid session tokens (steamLoginSecure, sessionid, optional oauthToken).
You can set/update them in account detail page (/accounts/:id) or they are imported if present in .maFile session payload.
Only active pending confirmations are shown in the queue view.
Internal bot endpoints are under /api/telegram/bot/* and protected by header:
x-telegram-bot-token: $TELEGRAM_BOT_TOKEN
- Install Docker + Docker Compose.
- Clone repository.
cp .env.example .envand set production secrets.- Change
ADMIN_PASSWORDfrom the bootstrap default before production start. - Configure
APP_URLandAPI_URLto match your real external origin/proxy layout. - Optionally set external reverse proxy to forward:
/-> frontend:3000/apiand/ws-> backend:3001
- If you want anti-bot registration, configure Cloudflare Turnstile keys in
.env. - Run
make deploy.
Optional bundled Nginx proxy:
docker compose --profile proxy up -d- Backend unit/API tests: Jest (
backend/tests/*). - Frontend unit tests: Vitest (
frontend/src/utils/format.test.ts). - E2E smoke: Cypress (
frontend/cypress/e2e/smoke.cy.ts).
For local host runs Cypress may require system dependencies (Xvfb). make test uses cypress/included Docker image to avoid host setup issues.
MIT (LICENSE).






