give every developer a way into the API that does not depend on one person

With DEBUG=0 and Kavenegar unconfigured, POST /api/auth/otp/request returns a
hard 503, so the only way to sign in was for someone with admin access to hand-
create an OTPCode row. That made every frontend developer's login depend on one
person being available.

TEST_LOGIN_PHONES is the pattern real apps use for exactly this (Firebase Auth's
fictional numbers, Apple's App Store demo account): a short allowlist of
phone:code pairs that skip SMS delivery entirely. Unlike DEV_MASTER_OTP it is
not "any phone with one code" and it is not tied to DEBUG -- each number carries
its own code, and everything else takes the normal OTP path untouched.

The code never appears in an API response, so knowing the number is not enough
to log in, and the existing per-phone login attempt limit still applies, so the
code cannot be brute-forced. Malformed configuration raises ImproperlyConfigured
at startup rather than being skipped silently, and /api/health reports how many
test numbers are open (count only) so they cannot be forgotten on a server that
has real users.

The code is capped at six digits because LoginRequestSerializer caps otp at
max_length=6; a longer code would have been rejected before reaching the check
and looked like the feature was broken.

The guest path the frontend uses today is untouched, and leaving the variable
empty reproduces the previous behaviour exactly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 00:52:57 +03:30
parent 8fce3b9548
commit 5aa10eb7b7
7 changed files with 253 additions and 0 deletions

View File

@@ -199,6 +199,41 @@ python manage.py generate_questions_from_chunks --subject ریاضی --grade ن
می‌شوند. محتوای واقعی از مسیرِ جدولِ بالا می‌رود، نه از این دستور.
### ورودِ توسعه‌دهنده‌ها به سرور (بدونِ کاوه‌نگار)
تا وقتی `KAVENEGAR_API_KEY` ست نشده، `POST /api/auth/otp/request` خطای صریحِ
۵۰۳ می‌دهد و با `DEBUG=0` هیچ‌کس نمی‌تواند وارد API شود. برای اینکه ورودِ تیمِ
فرانت به یک نفرِ خاص وابسته نباشد، `TEST_LOGIN_PHONES` را در `.env` سرور پر
کنید:
```bash
TEST_LOGIN_PHONES=09120000001:834512,09120000002:110947
```
هر جفت یک حسابِ تست است: برای آن شماره پیامکی ارسال نمی‌شود و همان کدِ ثابت
همیشه معتبر است. هر شماره‌ی دیگری مسیرِ عادیِ OTP را می‌رود، بی‌هیچ تغییری.
```bash
curl -X POST https://api.math.tadrisino2.ir/api/auth/otp/request \
-H 'Content-Type: application/json' -d '{"phone":"09120000001"}'
curl -X POST https://api.math.tadrisino2.ir/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"phone":"09120000001","otp":"834512"}'
```
چند نکته که عمدی‌اند:
- **این با `DEV_MASTER_OTP` یکی نیست.** آن کدِ `0000` هر شماره‌ای را قبول
می‌کند و فقط با `DEBUG=1` روشن می‌شود؛ این فقط همین چند شماره را، هرکدام با
کدِ خودش.
- **کد در پاسخِ API برنمی‌گردد.** کسی که شماره را بداند هم نمی‌تواند کد را از
سرور بیرون بکشد، و سقفِ تلاشِ ورودِ هر شماره روی این‌ها هم اعمال می‌شود.
- **`/api/health` تا وقتی این متغیر پر باشد هشدار می‌دهد** (تعداد، نه شماره و
نه کد) تا روی سروری که کاربرِ واقعی دارد فراموش نشود.
- **مسیرِ مهمان دست‌نخورده است.** فرانت که امروز با
`POST /api/auth/session` و `deviceId` کار می‌کند، هیچ تغییری نمی‌بیند.
- خالی گذاشتنِ این متغیر یعنی رفتارِ قبلی، مو‌به‌مو.
### متغیرهای محیطی موردنیاز
| متغیر | لازم در پروداکشن | توضیح |
|---|---|---|
@@ -207,6 +242,7 @@ python manage.py generate_questions_from_chunks --subject ریاضی --grade ن
| `DJANGO_ALLOWED_HOSTS` | **بله** | لیستِ host ها، کاما-جدا |
| `CACHE_URL` | **بله** | آدرسِ Redis، مثلاً `redis://host:6379/0` — برای rate limitِ مشترک وقتی `DEBUG=False` لازمه |
| `KAVENEGAR_API_KEY` | **بله** | ارسال واقعیِ OTP از طریق کاوه‌نگار |
| `TEST_LOGIN_PHONES` | نه (فقط تا وقتی تیم دارد توسعه می‌دهد) | شماره‌های تستِ ورود، بخشِ بالا |
| `GRAFANA_ADMIN_PASSWORD` | فقط اگه از استکِ Grafana استفاده می‌کنید | بخشِ Docker پایین رو ببینید |
### تنظیمات پروداکشن