تصميم نظام استعادة كلمة المرور ورموز OTP: تفادي هجمات التوقيت والتخمين وإدارة الحالة
بناء نظام استعادة كلمة مرور غير قابل للاختراق: توليد الرموز المشفرة، حماية التوقيت الزمني، تقييد محاولات التخمين، وحرق الرموز فور الاستخدام.
🔐 Forgot Password & OTP: لما Reset Password يبقى System Design
<img src="https://img.shields.io/badge/Security-Password%20Recovery-red" alt="Password Recovery"/> <img src="https://img.shields.io/badge/Authentication-OTP-blue" alt="OTP"/> <img src="https://img.shields.io/badge/Reliability-Rate%20Limiting-green" alt="Rate Limiting"/> <img src="https://img.shields.io/badge/Interview-System%20Design-purple" alt="System Design Interview"/>سؤال انترفيو شكله بسيط جدًا...
User نسي الباسورد وضغط Forgot Password. إيه اللي بيحصل؟
لكن ورا الشاشة الصغيرة دي فيه Security + Concurrency + Abuse Prevention + Session Management.
</div>📚 Table of Contents
- السؤال
- الإجابة السريعة
- أول خطوة: Forgot Password
- Account Enumeration
- Generate OTP
- هل كل OTP جديد يلغي القديم؟
- OTP Expiration
- OTP Storage
- ليه Redis مناسب؟
- Rate Limiting
- Brute Force
- Attempt Limits
- Verify OTP
- Double Click و Replay
- Reset Session
- Password Storage
- After Password Reset
- Refresh Tokens و Sessions
- Security Notifications
- Race Conditions
- State Machine
- Architecture
- Redis Key Design
- Database Design
- Failure Scenarios
- Common Wrong Answers
- Interview Answer
- Production Checklist
- Official Sources
🎤 السؤال
الـinterviewer يقول:
User نسي الباسورد، دخل الإيميل وضغط Send OTP. إيه اللي بيحصل؟
الإجابة السريعة:
Forgot Password
↓
Generic Response
↓
Rate Limit
↓
Generate Secure OTP / Reset Token
↓
Store Short-Lived Verification State
↓
Send Email
↓
User Enters OTP
↓
Rate Limit + Verify
↓
Consume OTP
↓
Create Restricted Reset Session
↓
Set New Password
↓
Invalidate Relevant Sessions
↓
Notify User
لكن كل سهم هنا وراه security decisions.
OWASP تعتبر Forgot Password functionality مصدرًا شائعًا للثغرات، وتوصي بأن تكون reset codes/tokens عشوائية، single-use، محدودة العمر، ومحمية من brute force، مع rate limiting وgeneric responses لمنع account enumeration. citeturn0search0
1️⃣ أول خطوة: Forgot Password
المستخدم يكتب:
ahmed@example.com
ويضغط:
Send OTP
الـbackend يستقبل:
POST /auth/forgot-password
لكن أول غلطة ممكن تعملها:
if user doesn't exist:
return "Email doesn't exist"
ليه؟
لأن attacker ممكن يجرب:
admin@example.com
user1@example.com
user2@example.com
...
ويعرف مين عنده account.
وده اسمه:
🔎 Account Enumeration
OWASP توصي بأن تكون الرسالة والزمن متقاربين سواء الحساب موجود أو غير موجود. citeturn0search0turn0search1
فبدل:
Email doesn't exist ❌
تقول:
If an account exists for this email,
you will receive instructions shortly.
حتى لو الإيميل مش موجود.
2️⃣ Generate OTP
لو الحساب موجود، السيرفر يولد OTP.
مثلاً:
123456
لكن متستخدمش:
Math.random()
لأغراض security-sensitive tokens.
استخدم:
Cryptographically Secure Random Generator
OWASP توصي بأن تكون reset tokens/codes مولدة باستخدام cryptographically secure randomness. citeturn0search0
🔢 هل 6 Digits كفاية؟
OTP من 6 أرقام عنده:
000000 → 999999
يعني تقريبًا:
1,000,000 possibilities
وده entropy حوالي:
20 bits
فلو مفيش rate limiting:
000001
000002
000003
...
المهاجم عنده فرصة في النهاية يجرب الكود الصحيح.
عشان كده:
Short OTP لازم يكون وراءه rate limiting + expiration + attempt limits.
NIST توضح أن OTPs القصيرة تحتاج rate limiting ضد online guessing، وأن OTP من 6 أرقام تقريبًا 20-bit entropy. citeturn0search2turn0search46
3️⃣ هل كل OTP جديد يلغي القديم؟
تخيل:
Request 1
↓
OTP = 123456
بعدها المستخدم ضغط:
Send OTP
مرة ثانية:
OTP = 654321
دلوقتي:
123456
654321
هل الاتنين صالحين؟
الأفضل في password reset flow إن يكون عندك verification challenge واحد active لكل recovery flow، وأن إصدار challenge جديد يبطل القديم.
يعني:
New OTP
↓
Invalidate previous challenge
↓
Create new challenge
فيبقى:
123456 → Invalid
654321 → Valid
وده يقلل نافذة استخدام الأكواد القديمة.
🧠 لكن فيه Race Condition هنا!
تخيل:
Request A → Generate OTP A
Request B → Generate OTP B
والـrequests وصلوا في نفس الوقت.
لو عملت:
DELETE old OTP
INSERT new OTP
بشكل غير آمن، ممكن يحصل:
A deletes
B deletes
A inserts
B inserts
فتطلع بأكثر من OTP active.
عشان كده لازم تصميم الـchallenge نفسه يكون concurrency-safe.
مثلاً ممكن تستخدم:
generation/version
+
atomic Redis operation
أو transaction/unique constraint في database.
الفكرة:
"Only one active reset challenge" لازم تكون invariant في السيستم، مش مجرد assumption في الكود.
4️⃣ OTP Expiration
OTP مش المفروض يعيش للأبد.
مثلاً:
OTP
TTL = 5 minutes
بعدها:
Expired
OWASP توصي بأن reset tokens/codes تكون time-limited وتنتهي بعد فترة مناسبة. citeturn0search0
لكن خلي بالك:
5 minutes
مش رقم سحري.
الـTTL يعتمد على:
Risk
Delivery latency
User experience
Channel
Threat model
5️⃣ OTP Storage
ناس كتير تعمل:
Database
userId: 10
otp: "123456"
expiresAt: ...
وده ممكن يشتغل.
لكن الـOTP نفسه secret مؤقت.
فالأفضل ألا تخزن القيمة plaintext إذا كان تصميمك يسمح بتخزين verifier مشتق منها.
مثلاً conceptually:
OTP entered
↓
HMAC / secure hash
↓
Compare with stored verifier
وبالتالي لو حد قرأ storage، مش بياخد الـOTP plaintext مباشرة.
خليك واعي إن hashing short OTP وحده لا يحل المشكلة لو المهاجم يقدر offline brute-force على hash بدون حماية؛ لذلك لازم يكون التخزين والـverification design مناسبين لطبيعة الـOTP ومدة حياته.
⚡ 6️⃣ ليه Redis مناسب؟
الـOTP حالة:
Temporary
Short-lived
High-read/write
Automatically expiring
وده use case مناسب جدًا لـRedis.
مثلاً conceptually:
reset:challenge:user:10
ويكون عنده:
TTL = 300 seconds
Redis يدعم key expiration، وبالتالي تقدر تخلي البيانات المؤقتة تنتهي تلقائيًا بدل cleanup job مستمر. citeturn0search0
🗂️ Redis Key Design
ممكن تخزن:
reset:otp:{userId}
بقيمة مثل:
{
"challengeId": "rch_123",
"otpHash": "...",
"attempts": 0,
"createdAt": "...",
"expiresAt": "..."
}
لكن في production ممكن تفصل keys:
reset:challenge:{challengeId}
reset:rate:{userId}
reset:attempts:{challengeId}
عشان كل جزء يبقى له TTL وpolicy مناسب.
🚦 7️⃣ Rate Limiting
المستخدم ضغط:
Send OTP
مرة.
مرتين.
10 مرات.
100 مرة.
لو مفيش rate limit:
100 emails
ممكن تتبعت لنفس الشخص.
وده:
Spam
+
Resource Exhaustion
+
Email Provider Cost
+
Abuse
OWASP توصي بحماية forgot-password requests من excessive automated submissions باستخدام rate limiting على الأقل، مع controls إضافية عند الحاجة. citeturn0search0turn0search8
🧱 Rate Limit مش لازم يبقى على IP فقط
لو عملت:
5 requests / minute / IP
المهاجم ممكن يغير IP.
فالأفضل تستخدم layers:
Per Account / Email
+
Per IP
+
Per Device / Session
+
Global / Provider Quota
OWASP توصي بتطبيق rate limits على أكثر من key بدل الاعتماد على IP فقط، وتذكر per-identity وper-IP وغيرها حسب السيناريو. citeturn0search5
مثلاً:
Email:
1 OTP / 60 sec
Email:
5 OTP / hour
IP:
20 reset requests / 10 min
الأرقام دي مجرد أمثلة؛ اختارها حسب الـthreat model والـUX.
🧨 8️⃣ Brute Force
المهاجم عنده:
000000
000001
000002
...
فلازم OTP verification نفسه يكون rate-limited.
مثلاً:
Failed attempts = 0
كل محاولة:
Wrong OTP
↓
attempts++
وعند threshold معين:
Invalidate challenge
ثم:
Request new OTP
NIST تفرض rate limiting على أنواع OTP عندما تكون مساحة الاحتمالات منخفضة، وOWASP توصي بالحماية من brute-force أثناء reset verification. citeturn0search2turn0search0
🔢 9️⃣ Attempt Limits
مثلاً:
Maximum attempts = 5
لو:
1 ❌
2 ❌
3 ❌
4 ❌
5 ❌
نعمل:
Challenge = Invalid
حتى لو بعد كده المستخدم عرف الكود القديم.
لكن خليك واعي:
الـ5 هنا example، مش security standard ثابت.
NIST الحالية تعتبر 100 محاولة حدًا أقصى عامًا لبعض أنواع authenticators، وتسمح بحدود أقل حسب النظام؛ في password recovery عالي الحساسية ممكن تختار threshold أقل بكثير. citeturn0search46
🔐 🔟 Verify OTP
المستخدم يدخل:
123456
الـbackend يعمل:
Find active challenge
↓
Check expiration
↓
Check attempts
↓
Verify OTP
↓
Consume challenge
↓
Create reset session
والترتيب مهم.
🚨 Double Click و Replay
تخيل:
User enters correct OTP
ويضغط:
Verify
Verify
والطلبين وصلوا تقريبًا مع بعض.
لو implementation سيئة:
Request A → OTP valid
Request B → OTP valid
الاتنين يعدوا.
وده ضد فكرة:
Single-use
عشان كده verification لازم يكون atomic قدر الإمكان:
Check valid
+
Consume
في عملية concurrency-safe واحدة.
مثلاً conceptually:
GET/compare
+
DELETE
مش كعمليتين منفصلتين معرضتين للrace.
ممكن تستخدم:
Redis Lua Script
أو atomic primitive مناسب.
♻️ Replay Attack
حتى لو attacker عرف OTP:
123456
بعد أول successful verification:
OTP = consumed
لازم:
123456 → invalid
OWASP توصي بأن reset tokens/codes تكون single-use ويتم invalidating بعد استخدامها. citeturn0search0
🎟️ 1️⃣1️⃣ Reset Session
دي نقطة ناس كتير بتفوتها.
بعد:
OTP Verified
متعملش:
isAuthenticated = true
وخلاص.
لأن المستخدم لسه أثبت:
Possession of recovery channel
مش بالضرورة إنه لازم يحصل له full login session في نفس اللحظة.
الأفضل تعمل:
Restricted Password Reset Session
مثلاً:
resetSessionId = rs_123
وتكون صلاحيتها:
Short-lived
Single-purpose
وتسمح فقط:
POST /auth/reset-password
مش:
GET /profile
POST /orders
DELETE /account
OWASP تصف PIN/password-reset flow بحيث بعد إثبات الـPIN يتم إنشاء limited session تسمح فقط بعملية reset. citeturn0search0
🔑 1️⃣2️⃣ Password Reset
بعد verification:
POST /auth/reset-password
مع:
{
"resetSession": "...",
"newPassword": "..."
}
السيرفر يتحقق من:
Reset session valid?
Not expired?
Not consumed?
User matches?
وبعدين يعمل password update.
🔒 Password Storage
ممنوع:
password = "123456"
وممنوع:
password = SHA256(password)
بشكل مباشر كـpassword storage design.
استخدم password hashing algorithm مصمم لتخزين passwords، مثل:
Argon2id
bcrypt
scrypt
مع salt مناسب وإعدادات مناسبة للـenvironment.
الـreset flow لازم يرجع لنفس secure password-storage policy المستخدمة في login.
🔄 1️⃣3️⃣ بعد تغيير الباسورد
هنا السؤال المهم:
هل الحساب أصبح آمنًا؟
مش بالضرورة.
تخيل attacker كان بالفعل عنده:
Refresh Token
أو:
Active Session
تغيير الباسورد وحده ممكن لا يبطل session قائم حسب الـarchitecture.
OWASP توصي بأن النظام يوفر خيار invalidate sessions أو يعمل ذلك تلقائيًا بعد reset. citeturn0search0
🚪 Refresh Tokens وSessions
بعد successful password reset، ممكن تعمل:
User Sessions
↓
Revoke
أو:
Refresh Tokens
↓
Revoke / Rotate
بحسب session architecture.
مثلاً:
Device A → Refresh Token
Device B → Refresh Token
Device C → Refresh Token
بعد password reset:
A → Revoked
B → Revoked
C → Revoked
ثم المستخدم يعمل:
Login
من جديد.
🧠 JWT نقطة مهمة
لو الـAccess Token نفسه JWT:
JWT
ومدة صلاحيته:
15 minutes
مش معنى إنك عملت:
revoke refresh tokens
إن JWT القديم اختفى فورًا.
لو الـaccess token stateless، ممكن يفضل صالح حتى expiration إلا لو عندك revocation/introspection strategy.
عشان كده password reset architecture لازم تفكر في:
Access Token Lifetime
+
Refresh Token Revocation
+
Session Version
+
Token Introspection / Revocation Strategy
🧩 Session Version Pattern
حل شائع:
User
sessionVersion = 12
JWT يحمل:
sessionVersion = 12
بعد password reset:
sessionVersion = 13
وبالتالي أي token يعتمد على:
12
يبقى غير صالح لو backend بيتحقق من version.
لكن ده يلغي جزء من statelessness لأنك محتاج lookup أو cached user/session state.
يعني:
Security trade-off.
📧 Security Notification
بعد reset:
Password changed successfully.
ممكن تبعت notification:
Your password was changed.
وده مفيد لو attacker هو اللي نفذ recovery.
لكن:
متبعتش الـpassword نفسه في email.
OWASP توصي بإبلاغ المستخدم بأن password reset تم، وعدم إرسال كلمة المرور عبر البريد. citeturn0search0
🕵️♂️ Suspicious Activity
في الأنظمة الحساسة، راقب:
10 reset requests
+
20 wrong OTPs
+
new device
+
new IP
ممكن تعمل:
CAPTCHA
Additional verification
Risk scoring
Temporary cooldown
Security alert
OWASP تذكر CAPTCHA وغيرها من controls كوسائل إضافية ضد automated abuse. citeturn0search0turn0search5
⚠️ Important: Don't Lock the Account
متعملش:
5 wrong OTP
↓
Account locked forever
لأن attacker ممكن يستخدم forgot-password flow كوسيلة يعمل بيها:
Denial of Service
على حساب شخص تاني.
OWASP تحذر من locking accounts كاستجابة لهجمات forgot-password لأنه يمكن استخدامه لتعطيل المستخدمين. citeturn0search0
الأفضل غالبًا:
Invalidate current reset challenge
بدل:
Lock entire account
🧠 Forgot Password State Machine
REQUESTED
↓
OTP_SENT
↓
WAITING_FOR_VERIFICATION
↓
VERIFIED
↓
RESET_SESSION_CREATED
↓
PASSWORD_CHANGED
وفي أي وقت:
OTP_SENT
↓
EXPIRED
أو:
WAITING
↓
TOO_MANY_ATTEMPTS
أو:
VERIFIED
↓
RESET_SESSION_EXPIRED
🏗️ Architecture
flowchart LR
U["User"]
F["Frontend"]
API["Auth API"]
DB[("User DB")]
R[("Redis")]
MAIL["Email Provider"]
S["Session / Token Store"]
OBS["Logs / Monitoring"]
U --> F
F -->|"Forgot Password"| API
API --> DB
API --> R
API --> MAIL
U -->|"Enter OTP"| F
F -->|"Verify OTP"| API
API --> R
API --> S
F -->|"Reset Password"| API
API --> DB
API --> S
API --> OBS
API --> OBS
🔄 Full Flow
User
↓
Forgot Password
↓
POST /auth/forgot-password
↓
Generic Response
↓
Rate Limit
↓
Find Account Internally
↓
Invalidate Existing Challenge
↓
Generate Secure OTP
↓
Store OTP Verifier + TTL
↓
Send Email
↓
User Enters OTP
↓
POST /auth/verify-otp
↓
Rate Limit
↓
Check Challenge
↓
Check Expiration
↓
Check Attempts
↓
Atomic Verify + Consume
↓
Create Restricted Reset Session
↓
POST /auth/reset-password
↓
Validate Reset Session
↓
Hash New Password
↓
Update Password
↓
Revoke / Invalidate Sessions
↓
Notify User
↓
Require Normal Login
🗃️ Database Design
مش لازم كل حاجة تكون في database.
ممكن يكون عندك:
Users
id
email
password_hash
session_version
password_changed_at
Password Reset Attempts / Audit
id
user_id
challenge_id
requested_at
verified_at
ip
user_agent
result
ولو الـOTP نفسه في Redis:
Redis
↓
Temporary verification state
والـDB:
Permanent account state
+
Audit
⚡ Redis Example
Conceptually:
SET reset:challenge:abc123 <otp-verifier> EX 300
وممكن:
reset:attempts:abc123
بـTTL مناسب.
لكن في production متعملش:
otp = "123456"
وتعتبر إن Redis وحده كفاية.
فكر في:
Secure generation
+
Secure storage
+
TTL
+
Attempt limits
+
Single use
🧨 Failure Scenarios
Scenario 1 — Send OTP 10 times
10 requests
↓
Rate limiter
↓
Reject / cooldown
Scenario 2 — Old OTP entered
OTP A
↓
New OTP B
↓
A = invalid
Scenario 3 — OTP expired
OTP
↓
TTL expired
↓
Reject
Scenario 4 — Wrong OTP 5 times
Attempts
↓
Threshold
↓
Challenge invalidated
Scenario 5 — Correct OTP submitted twice
Request A → consume
Request B → rejected
وده محتاج atomicity.
Scenario 6 — Email provider fails
ممكن يحصل:
OTP created
↓
Email failed
فمتعتبرش:
OTP sent
إلا لو delivery request نجح حسب semantics الخاصة بالـprovider.
وفي بعض الأنظمة ممكن تحتاج:
retry email
مع الحفاظ على نفس challenge أو إنشاء challenge جديد حسب policy.
Scenario 7 — Password changed
لازم تفكر:
Existing refresh tokens?
Existing sessions?
Access tokens?
Remember-me sessions?
API keys?
مش كل credential mechanism يتصرف بنفس الطريقة.
❌ Common Wrong Answers
❌ "هولد OTP في database وخلاص"
ممكن يشتغل، لكن مش بالضرورة أفضل design.
فكر في:
TTL
+
Cleanup
+
Scale
+
Security
❌ "هخزن OTP plaintext"
لو storage اتسرب، المهاجم ممكن يحصل على codes فعالة.
الأفضل تصميم secure verifier مناسب للـshort-lived secret.
❌ "OTP صحيح = Login"
مش بالضرورة.
الأفضل:
OTP verified
↓
Restricted reset session
↓
Password reset
❌ "أي OTP قديم ينفع لحد ما ينتهي"
ده يزيد نافذة الهجوم.
الأفضل غالبًا:
Latest active challenge only
❌ "هعمل rate limit على IP بس"
المهاجم ممكن يغير IP.
استخدم:
Per account
+
Per IP
+
Additional abuse signals
❌ "5 wrong attempts = lock account"
ده ممكن يتحول إلى DoS ضد الحساب.
الأفضل غالبًا invalidate الـreset challenge نفسه. citeturn0search0
❌ "بعد reset خلاص المستخدم logged in"
OWASP توصي بأن المستخدم يدخل من خلال login المعتاد بعد reset بدل auto-login، لأن ده يقلل تعقيد session handling. citeturn0search0
❌ "غيرت الباسورد وخلاص"
فكر في:
Refresh Tokens
Sessions
Access Tokens
Remember-me
API keys
🎯 Interview Answer
لو الـinterviewer قال:
"User نسي الباسورد ودخل الإيميل وضغط Send OTP. إيه اللي بيحصل؟"
ممكن تجاوب:
"أول حاجة هتعامل مع الـforgot-password endpoint بطريقة تمنع account enumeration، فهرد برسالة generic سواء الإيميل موجود أو لا، ومعايا rate limiting على مستوى الحساب والـIP. لو الحساب موجود، هولد OTP باستخدام cryptographically secure randomness، ويكون short-lived وsingle-use. لو المستخدم طلب OTP جديد، هبطل الـprevious challenge بحيث يكون عندي challenge واحد active، ولازم عملية الإصدار نفسها تكون concurrency-safe. هخزن verification state في Redis مثلًا مع TTL، ومش هخزن الـOTP plaintext لو أقدر أستخدم verifier مناسب. عند verify، هعمل rate limiting وعدد محاولات محدود، وأتأكد من expiration، وبعد النجاح أستهلك الـOTP atomically عشان أمنع replay أو double verification. بعدها مش هدي المستخدم login session عادي، لكن هعمل restricted password-reset session تسمح فقط بتغيير الباسورد. بعد تغيير الباسورد، هبطل أو أعمل invalidate للـsessions والـrefresh tokens حسب الـsession architecture، وأرسل security notification، والمستخدم يعمل login بشكل طبيعي."
لو سألك:
"طيب ليه Redis؟"
تقول:
"لأن الـOTP state temporary وقصير العمر وعنده TTL وhigh write/read volume، فRedis مناسب لتخزين الحالة المؤقتة مع automatic expiration، بينما الـdatabase تفضل للحالة الدائمة والـaudit."
ولو سألك:
"ماذا لو المستخدم ضغط Verify مرتين؟"
تقول:
"الـOTP لازم يكون single-use، وعمليّة verify + consume لازم تكون atomic أو concurrency-safe؛ أول request يستهلك الـchallenge والثاني يترفض."
🧠 Mental Model
احفظها:
Don't Reveal Account Existence
↓
Rate Limit
↓
Generate Secure Challenge
↓
Expire It
↓
Only One Active Challenge
↓
Limit Attempts
↓
Verify + Consume Atomically
↓
Create Restricted Reset Session
↓
Change Password Securely
↓
Invalidate Sessions
↓
Notify User
↓
Normal Login
وأهم 6 أسئلة تسألهم في أي Password Recovery System:
1. What if the user requests OTP 10 times?
2. What if two OTP requests happen simultaneously?
3. What if the attacker guesses OTPs?
4. What if Verify is submitted twice?
5. What if the password changes while an old session exists?
6. What if the reset flow crashes between verification and password update?
لو قدرت تجاوب على الستة دول، أنت مش بس عامل:
Forgot Password UI
أنت بدأت تفكر في:
Security
+
Concurrency
+
Abuse Prevention
+
Session Management
+
Distributed Systems
🏆 Production Checklist
Forgot Password
- Generic response
- No account enumeration
- Consistent response timing
- Rate limiting
- Abuse monitoring
OTP
- Cryptographically secure generation
- Short lifetime
- Single-use
- Latest challenge invalidates previous one
- Attempt limit
- Atomic verification/consumption
- Secure storage strategy
Redis
- TTL
- Separate rate-limit keys
- Challenge identifier
- Atomic operations where needed
- Recovery strategy if Redis unavailable
Password
- Secure password hashing
- Password policy
- No password in email
- Password changed timestamp
Sessions
- Refresh token revocation/rotation
- Existing session invalidation
- Access-token strategy considered
- Remember-me sessions considered
Security
- HTTPS
- Audit logging
- Suspicious activity detection
- CAPTCHA/risk controls when needed
- Email delivery abuse controls
📚 Official Sources
OWASP
- OWASP — Forgot Password Cheat Sheet
- OWASP — Authentication Cheat Sheet
- OWASP — Session Management Cheat Sheet
- OWASP — Business Logic Security Cheat Sheet
- OWASP — Email Validation and Verification Cheat Sheet
NIST
- NIST SP 800-63B — Authentication and Lifecycle Management
- NIST SP 800-63B-4 — Digital Identity Guidelines
Redis
<div align="center">
🔐 Forgot Password is not just "Send OTP".
It's an authentication recovery system.
Generate → Expire → Rate Limit → Verify → Consume → Reset → Revoke
</div>منشورات مقترحة
مشاريع ذات صلة
منصة تعليمية متكاملة تتيح إجراء الاختبارات المحاكاة المؤقتة، واستعادة جلسة الامتحان من التخزين المحلي، وتقييم قدرات الطلاب بتقارير تحليلية.