Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Game Backend API

An async FastAPI backend for a multiplayer party game: passwordless phone/OTP authentication, JWT sessions, a premium subscription tier, a question catalogue, and a self-hosted admin console.

Built and deployed to a Linux VPS behind PostgreSQL, serving a Unity mobile client. This repository contains the server and its admin panel.

Unity client ─┐
              ├─► FastAPI (JWT auth, premium gating) ─► PostgreSQL
Admin panel ──┘                     │
                                    └─► SMS gateway (OTP delivery)

Highlights

  • Passwordless auth. A phone number receives a one-time code by SMS; the first successful verification creates the account. No passwords are stored, so there is no password database to leak.
  • Rate limiting on OTP issue, with a correct Retry-After header, so the endpoint cannot be used to run up an SMS bill.
  • Single-use codes. A verified code is destroyed immediately, so a replayed request cannot mint a second token.
  • Role checks read the database, not the token. The JWT carries an admin claim, but get_current_admin re-reads the admins table on every request — revoking an admin takes effect instantly instead of at token expiry. There is a test for exactly that.
  • Premium is a time window, not a boolean. is_premium_active checks the flag and the expiry date, so a lapsed subscription loses access without a cleanup job.
  • Swappable SMS backend. With no credentials configured the OTP is written to the log instead of sent, so the project can be cloned and run end to end without a paid SMS account.
  • 19 tests covering the auth handshake, rate limiting, premium gating, cross-user access, and admin promotion/revocation.

The OTP handshake

sequenceDiagram
    participant C as Client
    participant A as API
    participant S as SMS gateway
    participant D as PostgreSQL

    C->>A: POST /auth/send_otp {phone}
    A->>A: rate-limit check (429 + Retry-After)
    A->>S: send 4-digit code
    A-->>C: {success: true}

    C->>A: POST /auth/verify_otp {phone, otp}
    A->>D: find or create user
    A->>D: is this phone in admins?
    A->>A: burn the code (single use)
    A-->>C: {token: JWT, isPremium}

    C->>A: any request + Bearer token
    A->>D: resolve user, re-check role
    A-->>C: response
Loading

Quick start

Runs against SQLite with no SMS account, so a clone works immediately.

git clone https://github.com/SaeedSabzeh/fastapi-game-backend.git
cd fastapi-game-backend

python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -r requirements-dev.txt

cp .env.example .env

Edit .env — for a local run these two lines are enough:

DATABASE_URL=sqlite+aiosqlite:///./dev.sqlite3
JWT_SECRET_KEY=<paste the output of the command below>
python -c "import secrets; print(secrets.token_urlsafe(48))"

Create the tables and start the server:

python -m scripts.init_db
uvicorn app.main:app --reload

With SMS_API_KEY blank, POST /auth/send_otp prints the code to the console:

SMS disabled - OTP for 09121112233 is 3614

Use that code with /auth/verify_otp to get a token. To reach the admin routes, insert your phone into the admins table after your first login.

Run the tests:

pytest

API

Method Path Auth Purpose
GET / Health check
POST /auth/send_otp Send a login code (rate limited)
POST /auth/verify_otp Verify the code, create account, return JWT
POST /auth/validateToken Bearer Session check on client start-up
POST /auth/upgrade_premium Bearer Grant premium, re-issue the token
GET /api/questions Shared question pool, by game mode
GET /api/my-questions Premium The caller's own questions
POST /api/my-questions Premium Author a question
DELETE /api/my-questions/{id} Premium Delete one's own question
GET / POST /user/customizations Bearer Per-user client settings (JSON blob)
GET POST PUT DELETE /admin/questions Admin Manage the shared pool
GET /admin/users Admin List players and subscription state
GET /admin/list-admins Admin List admins
POST /admin/add-admin Admin Promote a registered player
POST /admin/remove-admin Admin Revoke admin rights

Layout

app/
  config.py       Settings from env; refuses to boot without secrets
  database.py     SQLAlchemy Core tables + async connection
  models.py       Pydantic request/response schemas
  crud.py         Database access, no request objects
  auth.py         OTP flow, JWT creation, auth dependencies
  sms.py          SMS delivery, console backend for development
  main.py         App wiring: CORS, routers, lifespan, static panel
  routers/
    questions.py, customization.py, admin.py
scripts/init_db.py   Table creation
static/index.html    Admin console (vanilla JS, no build step)
tests/               19 pytest cases

Configuration

Every setting is an environment variable; see .env.example. DATABASE_URL and JWT_SECRET_KEY are required and have no defaults — the app raises a ValidationError at import time rather than starting with a fallback secret that could reach production by accident.


Known limitations

Kept deliberately visible rather than papered over:

  • OTP store is in-process. Codes live in a module-level dict, which is fine for a single worker but breaks under multiple uvicorn workers. Redis with a TTL is the drop-in replacement.
  • 4-digit codes with a 60-second issue limit. The short code is a product decision for mobile UX; it needs a per-phone attempt counter on /auth/verify_otp before a wider launch.
  • No migration tool. scripts/init_db.py creates tables from the metadata; Alembic is the next step once the schema starts changing in production.
  • upgrade_premium has no payment step. It grants 30 days directly, standing in for the payment-provider callback.
  • Rate limiting is per-process, for the same reason as the OTP store.

Copyright

© 2026 Saeed Sabzeh. All rights reserved.

This source is published for portfolio and review purposes. No licence is granted to use, copy, modify, or distribute it.

About

Async FastAPI backend for a multiplayer party game — passwordless phone/OTP auth, JWT sessions, a premium tier, and an admin console. Deployed to a VPS behind PostgreSQL, serving a Unity mobile client.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages