Skip to content

Commit fe20a38

Browse files
committed
Initial commit: Steam web authenticator project
0 parents  commit fe20a38

103 files changed

Lines changed: 28811 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# App
2+
NODE_ENV=development
3+
APP_URL=http://localhost:3000
4+
API_URL=http://localhost:3001
5+
JWT_SECRET=change_me_super_secret_jwt
6+
COOKIE_SECRET=change_me_super_secret_cookie
7+
ENCRYPTION_KEY=change_me_32_bytes_minimum
8+
9+
# MySQL
10+
DB_HOST=mysql
11+
DB_PORT=3306
12+
DB_NAME=steamguard
13+
DB_USER=steamguard
14+
DB_PASSWORD=steamguard
15+
DB_ROOT_PASSWORD=steamguard_root
16+
17+
# Telegram
18+
TELEGRAM_BOT_TOKEN=change_me_telegram_bot_token
19+
TELEGRAM_BOT_USERNAME=your_bot_username_without_@
20+
21+
# Optional seed admin (defaults: admin@admin.com/admin123)
22+
ADMIN_EMAIL=admin@admin.com
23+
ADMIN_PASSWORD=admin123
24+
25+
# Steam polling
26+
STEAM_POLL_INTERVAL_SEC=20

.github/workflows/ci.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: ['**']
6+
pull_request:
7+
8+
jobs:
9+
backend:
10+
runs-on: ubuntu-latest
11+
defaults:
12+
run:
13+
working-directory: backend
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: actions/setup-node@v4
17+
with:
18+
node-version: 20
19+
cache: npm
20+
cache-dependency-path: backend/package-lock.json
21+
- run: npm install
22+
- run: npm run lint
23+
- run: npm run test
24+
25+
frontend:
26+
runs-on: ubuntu-latest
27+
defaults:
28+
run:
29+
working-directory: frontend
30+
steps:
31+
- uses: actions/checkout@v4
32+
- uses: actions/setup-node@v4
33+
with:
34+
node-version: 20
35+
cache: npm
36+
cache-dependency-path: frontend/package-lock.json
37+
- run: npm install
38+
- run: npm run lint
39+
- run: npm run test

.gitignore

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Node
2+
node_modules/
3+
npm-debug.log*
4+
yarn-debug.log*
5+
yarn-error.log*
6+
pnpm-debug.log*
7+
8+
# Build artifacts
9+
dist/
10+
coverage/
11+
.vite/
12+
.cache/
13+
*.tsbuildinfo
14+
15+
# Env
16+
.env
17+
.env.local
18+
.env.save
19+
20+
# Docker
21+
mysql_data/
22+
23+
# Editors
24+
.vscode/
25+
.idea/
26+
*.swp
27+
*.swo
28+
.DS_Store
29+
30+
# Cypress
31+
frontend/cypress/videos/
32+
frontend/cypress/screenshots/
33+
34+
# Python
35+
__pycache__/
36+
*.pyc
37+
.venv/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 SteamGuard Web Contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
COMPOSE = docker compose
2+
BASE = -f docker-compose.yml
3+
DEV = -f docker-compose.yml -f docker-compose.dev.yml
4+
5+
.PHONY: dev build up deploy down lint test logs
6+
7+
dev:
8+
$(COMPOSE) $(DEV) up --build
9+
10+
build:
11+
$(COMPOSE) $(BASE) build
12+
13+
up:
14+
$(COMPOSE) $(BASE) up -d --build
15+
16+
deploy: up
17+
18+
down:
19+
$(COMPOSE) $(BASE) down --remove-orphans
20+
$(COMPOSE) $(DEV) down --remove-orphans
21+
22+
lint:
23+
$(COMPOSE) $(DEV) run --rm backend npm run lint
24+
$(COMPOSE) $(DEV) run --rm frontend npm run lint
25+
26+
test:
27+
$(COMPOSE) $(DEV) up -d mysql backend frontend
28+
$(COMPOSE) $(DEV) run --rm backend npm run test
29+
$(COMPOSE) $(DEV) run --rm cypress
30+
31+
logs:
32+
$(COMPOSE) $(BASE) logs -f --tail=200

README.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# SteamGuard Web
2+
3+
Open-source web implementation of Steam Desktop Authenticator (SDA) with multi-user and multi-account support.
4+
5+
- Backend: Fastify + TypeScript (API-first REST + WebSocket notifications)
6+
- Frontend: React + Vite + Tailwind + PWA
7+
- DB: MySQL 8 in Docker with encrypted MA files
8+
- Bot: Aiogram (single Telegram bot for all users)
9+
- License: MIT
10+
11+
## Why this stack
12+
13+
- **Fastify (instead of Express)**: lower overhead and better throughput for polling/real-time workloads.
14+
- **TypeScript on backend/frontend**: safer refactoring and better API contracts.
15+
- **MySQL 8 + Docker internal network**: relational consistency, easy VPS deploy, DB isolated from public access.
16+
- **React + Vite + Tailwind**: fast DX + responsive UI + simple theming.
17+
- **Vite PWA plugin**: installable app + service worker caching.
18+
- **Aiogram bot**: mature async Telegram framework for command and deep-link flows.
19+
20+
## Key features
21+
22+
- MA file import/export (`.maFile`) with AES-256 encryption in DB.
23+
- Multi-user + unlimited Steam accounts per user.
24+
- Steam code generation from `shared_secret`.
25+
- Trade/login confirmations API + queue + manual confirm/reject.
26+
- Auto-confirm toggle with per-account delay (0-60s).
27+
- Telegram OAuth-like login flow via bot deep-link.
28+
- Telegram account linking with `/add=<code>` (15 min TTL).
29+
- Telegram commands: `/accounts`, `/codes`, `/confirm <trade_id>`, `/status`.
30+
- JWT cookie sessions, CSRF protection, Helmet, bcrypt, brute-force guard via `rate-limiter-flexible`.
31+
- i18n EN/RU + light/dark theme.
32+
- Admin panel with global registration toggle.
33+
- Swagger docs at `/api-docs`.
34+
35+
## Default admin
36+
37+
- Email: `admin@admin.com`
38+
- Password: `admin123`
39+
40+
(Override with `.env`: `ADMIN_EMAIL`, `ADMIN_PASSWORD`)
41+
42+
## Architecture
43+
44+
- Monolith API-first backend (`/api/...`) + WS (`/ws`) on port `3001`.
45+
- Frontend SPA on port `3000`.
46+
- Frontend server (Vite/Nginx) proxies `/api` and `/ws` to backend.
47+
- MySQL is isolated in internal Docker network and not exposed externally.
48+
- Optional reverse proxy service (`nginx`) in compose profile `proxy`.
49+
50+
## Project structure
51+
52+
```text
53+
.
54+
├── backend/ # Fastify API (TypeScript)
55+
├── frontend/ # React + Vite + Tailwind + PWA
56+
├── bot/ # Aiogram Telegram bot
57+
├── docker/
58+
│ ├── mysql/init.sql # Schema bootstrap
59+
│ └── nginx/nginx.conf # Reverse proxy example
60+
├── docker-compose.yml
61+
├── docker-compose.dev.yml
62+
├── Makefile
63+
├── .env.example
64+
└── README.md
65+
```
66+
67+
## Quick start
68+
69+
```bash
70+
cp .env.example .env
71+
make dev
72+
```
73+
74+
Open:
75+
- Frontend: `http://localhost:3000`
76+
- Backend API: `http://localhost:3001`
77+
- Swagger: `http://localhost:3001/api-docs`
78+
79+
## Make targets
80+
81+
- `make dev` - Docker dev stack with hot reload.
82+
- `make build` - build production images.
83+
- `make up` - run production stack detached.
84+
- `make deploy` - alias for `make up`.
85+
- `make down` - stop and remove containers.
86+
- `make lint` - backend + frontend lint.
87+
- `make test` - backend Jest + frontend Cypress (via docker service).
88+
- `make logs` - follow container logs.
89+
90+
## Environment variables
91+
92+
See `.env.example`.
93+
94+
Core variables:
95+
- `DB_*` MySQL connection and bootstrap user credentials.
96+
- `JWT_SECRET`, `COOKIE_SECRET`, `ENCRYPTION_KEY` security secrets.
97+
- `APP_URL`, `API_URL` frontend/backend origins.
98+
- `TELEGRAM_BOT_TOKEN`, `TELEGRAM_BOT_USERNAME` bot settings.
99+
- `STEAM_POLL_INTERVAL_SEC` auto-confirm polling interval.
100+
101+
If `TELEGRAM_BOT_TOKEN` is empty or starts with `change_me`, bot service stays in disabled idle mode (no crash, API stays up).
102+
103+
## Security model
104+
105+
- **MA encryption**: AES-256-GCM per user.
106+
- **Key derivation**: per-user key derived from bcrypt password hash + global `ENCRYPTION_KEY`.
107+
- **Auth**: JWT in HTTP-only cookie.
108+
- **CSRF**: double-submit protection for mutating endpoints.
109+
- **Brute-force/DoS**: `rate-limiter-flexible` in auth/write paths.
110+
- **Hardening**: `helmet`, CORS with credentials.
111+
- **DB isolation**: MySQL only on internal Docker network (`db_internal`).
112+
113+
## Telegram flows
114+
115+
### Link Telegram account
116+
1. In Settings click "Generate /add code".
117+
2. Send `/add=<code>` to bot within 15 min.
118+
3. Bot binds `telegram_user_id` to your web user.
119+
120+
### Login via Telegram
121+
1. On login page click "Login via Telegram".
122+
2. Open bot deep-link (`/start login_<code>`).
123+
3. Bot confirms code.
124+
4. Web page polls and creates session automatically.
125+
126+
## Steam confirmations notes
127+
128+
Steam mobile confirmations require valid session tokens (`steamLoginSecure`, `sessionid`, optional `oauthToken`).
129+
You can set/update them in account detail page (`/accounts/:id`) or they are imported if present in `.maFile` session payload.
130+
131+
## Bot API integration
132+
133+
Internal bot endpoints are under `/api/telegram/bot/*` and protected by header:
134+
135+
- `x-telegram-bot-token: $TELEGRAM_BOT_TOKEN`
136+
137+
## Screenshots (descriptive)
138+
139+
Planned screenshot set (see `docs/screenshots/README.md`):
140+
141+
1. `login.png`: login/register card with Telegram and Passkey actions.
142+
2. `dashboard.png`: account/notification/live cards + real-time events list.
143+
3. `accounts.png`: multi-account table with import/code/export/details actions.
144+
4. `account-detail.png`: confirmation queue with confirm/reject and session fields.
145+
5. `settings.png`: language/theme/twofa/telegram/api-key controls.
146+
6. `admin.png`: registration toggle and user overview.
147+
7. `logs.png`: activity feed for trade/login/code/system events.
148+
149+
## Deployment on VPS
150+
151+
1. Install Docker + Docker Compose.
152+
2. Clone repository.
153+
3. `cp .env.example .env` and set production secrets.
154+
4. Optionally set external reverse proxy to forward:
155+
- `/` -> frontend `:3000`
156+
- `/api` and `/ws` -> backend `:3001`
157+
5. Run `make deploy`.
158+
159+
Optional bundled Nginx proxy:
160+
161+
```bash
162+
docker compose --profile proxy up -d
163+
```
164+
165+
## Testing
166+
167+
- Backend unit/API tests: Jest (`backend/tests/*`).
168+
- Frontend unit tests: Vitest (`frontend/src/utils/format.test.ts`).
169+
- E2E smoke: Cypress (`frontend/cypress/e2e/smoke.cy.ts`).
170+
171+
For local host runs Cypress may require system dependencies (`Xvfb`). `make test` uses `cypress/included` Docker image to avoid host setup issues.
172+
173+
## License
174+
175+
MIT (`LICENSE`).

backend/.dockerignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
dist
3+
coverage
4+
npm-debug.log*

backend/.eslintrc.cjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = {
2+
root: true,
3+
parser: '@typescript-eslint/parser',
4+
plugins: ['@typescript-eslint'],
5+
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
6+
env: {
7+
node: true,
8+
es2022: true,
9+
jest: true
10+
},
11+
ignorePatterns: ['dist', 'node_modules'],
12+
rules: {
13+
'@typescript-eslint/no-explicit-any': 'off'
14+
}
15+
};

backend/Dockerfile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
FROM node:20-alpine AS base
2+
WORKDIR /app
3+
COPY package*.json ./
4+
5+
FROM base AS development
6+
RUN npm install
7+
COPY . .
8+
EXPOSE 3001
9+
CMD ["npm", "run", "dev"]
10+
11+
FROM base AS build
12+
RUN npm ci
13+
COPY . .
14+
RUN npm run build
15+
16+
FROM node:20-alpine AS production
17+
WORKDIR /app
18+
ENV NODE_ENV=production
19+
COPY package*.json ./
20+
RUN npm ci --omit=dev
21+
COPY --from=build /app/dist ./dist
22+
EXPOSE 3001
23+
CMD ["node", "dist/index.js"]

backend/jest.config.cjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/** @type {import('jest').Config} */
2+
module.exports = {
3+
preset: 'ts-jest',
4+
testEnvironment: 'node',
5+
roots: ['<rootDir>/tests'],
6+
setupFiles: ['<rootDir>/tests/setup.ts'],
7+
moduleFileExtensions: ['ts', 'js', 'json'],
8+
clearMocks: true,
9+
collectCoverageFrom: ['src/**/*.ts', '!src/index.ts']
10+
};

0 commit comments

Comments
 (0)