WisarWisar
Dasturlash kitobi/5-QISM — Nodejs17 daqiqa

5.18-bob: OTP va SMS integratsiyasi (Eskiz / Play Mobile)

5-QISM — Node.js Backend · 18-mavzu


1. Kirish va motivatsiya

Autentifikatsiya tizimini (5.15–5.17: parol, JWT, RBAC) qurdik. Endi O'zbekistonda juda keng tarqalgan, deyarli har bir ilovada uchraydigan amaliy mavzuni — telefon raqami orqali tasdiqlash (OTP) va SMS yuborish ni — o'rganamiz. O'zbekistonda ko'p ilova email emas, telefon raqami orqali ishlaydi: ro'yxatdan o'tish, login, parolni tiklash — hammasi SMS kodi bilan tasdiqlanadi.

OTP (One-Time Password — bir martalik parol) — qisqa muddatga amal qiladigan, bir marta ishlatiladigan kod (odatda 4-6 raqam). Foydalanuvchi telefon raqamini kiritadi, serverga SMS orqali kod keladi, uni kiritib o'zini tasdiqlaydi. Bu — telefon raqami haqiqatan o'sha odamniki ekanligini isbotlaydi (raqamga kirish — egalik isboti).

SMS yuborish uchun SMS provayder kerak — telefon operatorlariga ulangan xizmat. O'zbekistonda eng mashhurlari: Eskiz.uz va Play Mobile (playmobile.uz). Ular REST API 5.7-bob beradi — siz so'rov yuborasiz, ular SMS yetkazadi. Bu bobda OTP'ning to'liq oqimini (yaratish, yuborish, tekshirish, muddat, rate limiting) va Eskiz/Play Mobile integratsiyasini quramiz.

O'xshatish: OTP — bankomatdagi tasdiqlash kodi. Karta (telefon raqami) bor, lekin pul yechishdan oldin telefoningizga bir martalik kod keladi — uni kiritasiz. Kod — "bu haqiqatan siz" degan isbot (telefon sizdaligi). Kod qisqa muddatli (2-3 daqiqa) va bir martalik (ishlatilsa, bekor). Hatto kimdir kartani o'g'irlasa ham, telefoningizdagi kodsiz pul yecholmaydi.

Nega muhim?

  • O'zbekiston bozori — ilovalar telefon + SMS bilan ishlaydi; deyarli majburiy.
  • Xavfsizlik (14) — ikki bosqichli tasdiqlash; telefon egaligini isbotlash.
  • Amaliy — Eskiz/Play Mobile integratsiyasi — real ish ko'nikmasi.
  • Auth davomi — 5.15-5.17 ga telefon/OTP qo'shadi.

2. Nazariya — chuqur tushuntirish

2.1. OTP nima va qanday ishlaydi

OTP — bir martalik, qisqa muddatli kod. Asosiy g'oya: foydalanuvchi telefon raqamiga kirish imkoni borligini isbotlaydi (kod faqat o'sha telefonga keladi):

text
  OTP turlari:
  SMS OTP   — telefonga SMS (bu bob; O'zbekistonda asosiy)
  Email OTP — emailga kod 5.19-bob
  TOTP      — Google Authenticator (vaqtga bog'liq, SMS'siz — ilg'or)

2.2. OTP oqimi (umumiy rasm)

text
  1. Foydalanuvchi telefon raqamini kiritadi (+998901234567)
  2. Server: tasodifiy 6 xonali kod yaratadi (masalan 384726)
  3. Server: kodni HASH qilib, telefon + muddat bilan saqlaydi (DB/Redis — 2.6)
  4. Server: SMS provayder orqali kodni yuboradi (Eskiz — 2.9)
  5. Foydalanuvchi: SMS'dagi kodni kiritadi
  6. Server: kiritilgan kodni saqlangan bilan solishtiradi + muddat tekshiradi
  7. To'g'ri  tasdiqlandi (token beriladi — 5.15); noto'g'ri  xato
text
  ┌──────┐ telefon ┌────────┐  kod yaratadi+saqlaydi   ┌──────────┐
  │Client│────────▶│ Server │─────────────────────────▶│SMS provayder│
  │      │         │        │                          │ (Eskiz)  │
  └──────┘         └────────┘                          └─────┬────┘
     ▲                                                        │ SMS
     └────────────────── kod (SMS) ◀──────────────────────────┘
  Client kodni kiritadi  Server tekshiradi  token (5.15)

2.3. OTP kodini yaratish (tasodifiy, xavfsiz)

Kod tasodifiy va bashorat qilib bo'lmaydigan bo'lishi kerak (14):

js
import crypto from "node:crypto";              // (5.3)

//  Math.random — bashorat qilinadi (xavfsiz emas — 14)
const yomon = Math.floor(Math.random() * 900000) + 100000;

//  crypto — xavfsiz tasodif (5.3)
const kod = crypto.randomInt(100000, 1000000);   // 6 xonali (100000-999999)

crypto.randomInt ishlating (Math.random emas — 5.3): Math.random kriptografik xavfsiz emas (bashorat qilinishi mumkin — 14). OTP — xavfsizlik elementi, shuning uchun crypto.

2.4. OTP uzunligi va muddati

text
  Uzunlik:  4-6 raqam (6 — xavfsizroq; 4 — qulayroq, kamroq xavfsiz)
  Muddat:   1-5 daqiqa (qisqa — xavfsiz; juda qisqa — foydalanuvchi ulgurmaydi)
            O'zbekistonda odatda 2-3 daqiqa

Balans: qisqa muddat — xavfsizroq (o'g'irlangan kod tez yaroqsiz), lekin foydalanuvchi SMS kelishini kutib, kiritishga ulgurishi kerak. 2 daqiqa — ko'p holatda yaxshi. Muddat tugagach — qayta so'rash.

2.5. OTP'ni hash qilib saqlash (xavfsizlik — 14)

OTP — vaqtinchalik parol, shuning uchun uni ochiq saqlamang (parol kabi — 5.15):

text
   DB'da ochiq kod: { telefon, kod: "384726" }
      DB sizsa yoki ichki xodim ko'rsa — kodni biladi (14)

   DB'da hash: { telefon, kodHash: hash("384726"), expiresAt }
      solishtirishda kiritilgan kodni hash qilib taqqoslaymiz

Kichik loyihada ba'zan ochiq saqlanadi (kod qisqa muddatli), lekin hash — to'g'ri amaliyot (14). bcrypt 5.15-bob yoki crypto hash 5.3-bob.

2.6. OTP'ni qayerda saqlash (Redis — ideal)

OTP — vaqtinchalik (2-3 daqiqa). Uni saqlash uchun ideal joy — Redis 5.21-bob, chunki Redis avtomatik muddat (TTL) beradi:

text
  Redis:  SET otp:+998901234567 <hash> EX 120   (120 sekund = 2 daqiqa)
           2 daqiqadan keyin Redis O'ZI o'chiradi (muddat — 2.4)

  DB (6): { telefon, kodHash, expiresAt, urinishlar }
           muddatni qo'lda tekshirasiz; eski yozuvlarni tozalash kerak

Redis afzal 5.21-bob: TTL bilan avtomatik o'chadi (tozalash kerak emas); tez (xotirada). DB ham ishlaydi (muddatni qo'lda tekshirasiz). Redis bo'lmasa — DB; bo'lsa — Redis.

2.7. Rate limiting (eng muhim — suiiste'molni to'xtatish — 14)

OTP — eng ko'p suiiste'mol qilinadigan nuqta (14). Ikki xavf bor:

text
  1. SMS bombing — hacker bir raqamga minglab OTP so'raydi (SMS pul turadi!
      sizga katta hisob; foydalanuvchiga spam)
     Yechim: bir raqamga SMS so'rovini cheklash (masalan 1 daqiqada 1 marta, kuniga 5)

  2. Kod brute-force — hacker 6 xonali kodni minglab marta sinaydi (000000-999999)
     Yechim: noto'g'ri urinishlarni cheklash (masalan 3-5 marta, keyin bekor)

Rate limiting MAJBURIY (14): OTP'siz rate limiting — pul (SMS xarajati) va xavfsizlik falokati. SMS so'rovini (telefon bo'yicha) va tekshirish urinishini (kod bo'yicha) cheklash shart. Bu — eng ko'p e'tibordan chetda qoladigan, lekin eng muhim qism.

2.8. SMS provayder nima (Eskiz / Play Mobile)

SMS provayder — telefon operatorlariga (Beeline, Ucell, UMS — 0.4) ulangan xizmat; siz API orqali so'rov yuborasiz, ular SMS yetkazadi:

text
  Sizning botingiz  SMS provayder API (Eskiz)  operator  foydalanuvchi telefoni

  O'zbekiston provayderlari:
  Eskiz.uz       — eng mashhur; token-based REST API 2.9-bob
  Play Mobile    — playmobile.uz; XML/JSON API 2.10-bob
  (boshqalar: Twilio — xalqaro, qimmatroq)

Muhim: SMS matni provayder/operatorda oldindan tasdiqlangan shablon (template) bo'lishi kerak (O'zbekistonda qoida). Ixtiyoriy matn yubora olmaysiz — avval shablonni provayderdan tasdiqlatasiz ("Tasdiqlash kodi: {code}").

2.9. Eskiz.uz integratsiyasi (token-based)

Eskiz REST API 5.7-bob, token bilan ishlaydi:

text
  1. Login: POST https://notify.eskiz.uz/api/auth/login
     body: { email, password }   javob: { data: { token } }
      token olinadi (muddati bor — yangilanadi)

  2. SMS yuborish: POST https://notify.eskiz.uz/api/message/sms/send
     header: Authorization: Bearer <token>
     body: { mobile_phone: "998901234567", message: "Kod: 384726", from: "4546" }

Token boshqaruvi: Eskiz token muddati bor (~30 kun); kodda uni saqlab (kesh — 5.21), muddati tugaganda yangilash kerak. Email/parol — .envda (5.8, 14).

2.10. Play Mobile integratsiyasi (qisqacha)

Play Mobile (playmobile.uz) — boshqa API (asosan login/parol bilan, JSON):

text
  POST https://send.smsxabar.uz/broker-api/send
  Basic Auth (login:parol)
  body: { messages: [{ recipient: "998901234567", "message-id": "...",
          sms: { originator: "3700", content: { text: "Kod: 384726" } } }] }

Ikkala provayder ham printsipial bir xil: autentifikatsiya (token/basic) + SMS yuborish so'rovi. Kodda abstraksiya (interfeys) qiling — provayderni almashtirish oson bo'lsin (2.13, 9: SOLID).

2.11. OTP bilan auth oqimi (login/ro'yxat)

OTP autentifikatsiya bilan birlashadi 5.15-bob:

text
  RO'YXAT/LOGIN (telefon orqali):
  1. POST /auth/send-otp { telefon }  OTP yuboriladi 2.2-bob
  2. POST /auth/verify-otp { telefon, kod } 
     kod to'g'ri bo'lsa:
       - foydalanuvchi bor  login (token ber — 5.15, 5.16)
       - foydalanuvchi yo'q  yangi yarat (ro'yxat) + token
   OTP login va ro'yxatni BIRLASHTIRADI (parolsiz auth!)

Bu — O'zbekistonda eng keng tarqalgan: parolsiz, faqat telefon + OTP. Parol o'rniga — har safar (yoki birinchi marta) SMS kodi.

2.12. OTP xavfsizlik amaliyotlari (14)

text
   Kod crypto bilan (Math.random emas — 2.3, 14)
   Kodni hash qilib saqlang 2.5-bob; muddat qisqa (2-3min — 2.4)
   Rate limiting: SMS so'rovi (telefon) + tekshirish urinishi (kod) — 2.7, 14
   Urinish tugaganda kodni bekor qiling (yangi so'rasin)
   Ishlatilgan kodni darrov bekor (bir martalik — 2.1)
   Provayder kalitlari .env'da (14, 5.8)
   Kodni javobda/log'da QAYTARMANG/yozmang (14, 5.12)
   Telefon raqamini validatsiya (format — 5.9)

2.13. Provayder abstraksiyasi (SOLID — 9)

Provayderni to'g'ridan ishlatmang — interfeys orqali (provayderni almashtirish oson bo'lsin — 9: SOLID):

text
  smsService.send(telefon, matn)
       │
       ├─▶ EskizProvider     (hozir)
       └─▶ PlayMobileProvider (kelajakda — kodni o'zgartirmasdan)

   biznes logika "Eskiz"ni bilmaydi, faqat "SMS yubor" deydi (9.2: DIP)

3. Sintaksis — tez ma'lumotnoma

js
import crypto from "node:crypto";

// OTP yaratish (2.3)
const kod = crypto.randomInt(100000, 1000000);   // 6 xonali

// Saqlash (Redis — 2.6)
await redis.set(`otp:${tel}`, hash(kod), "EX", 120);   // 2 daqiqa TTL

// Eskiz SMS (2.9)
await axios.post("https://notify.eskiz.uz/api/message/sms/send",
  { mobile_phone, message, from: "4546" },
  { headers: { Authorization: `Bearer ${token}` } });

// Oqim 2.11-bob: send-otp  verify-otp  token (5.15)
// Rate limiting 2.7-bob MAJBURIY

4. Batafsil kod namunalari

Misol 1 — OTP yaratish va hash (2.3, 2.5)

js
import crypto from "node:crypto";

// Xavfsiz 6 xonali kod (2.3)
export const otpYarat = () => crypto.randomInt(100000, 1000000).toString();

// Kodni hash qilish (saqlash uchun — 2.5)
export const otpHash = (kod) =>
  crypto.createHash("sha256").update(kod).digest("hex");   // (5.3)

// Solishtirish
export const otpTekshir = (kiritilgan, saqlanganHash) =>
  otpHash(kiritilgan) === saqlanganHash;

Misol 2 — Eskiz provayder (token + yuborish — 2.9)

js
import axios from "axios";                       // (2.18-JS)
import { config } from "../config/index.js";     // (5.8)

let eskizToken = null;                            // keshlangan token (2.9)

// Token olish/yangilash
async function tokenOl() {
  if (eskizToken) return eskizToken;
  const { data } = await axios.post("https://notify.eskiz.uz/api/auth/login", {
    email: config.eskiz.email,                   // .env (14, 5.8)
    password: config.eskiz.password,
  });
  eskizToken = data.data.token;
  return eskizToken;
}

// SMS yuborish (2.9)
export async function eskizSms(telefon, matn) {
  try {
    let token = await tokenOl();
    await axios.post("https://notify.eskiz.uz/api/message/sms/send",
      { mobile_phone: telefon.replace("+", ""), message: matn, from: "4546" },
      { headers: { Authorization: `Bearer ${token}` } }
    );
  } catch (err) {
    if (err.response?.status === 401) {           // token muddati tugadi (2.9)
      eskizToken = null;                          // qayta olish
      return eskizSms(telefon, matn);             // bir marta qayta urin
    }
    throw err;                                    // (5.10)
  }
}

Misol 3 — Provayder abstraksiyasi (SOLID — 2.13)

js
// SMS xizmati — provayderni yashiradi (9.2: DIP, 2.13)
class SmsService {
  constructor(provider) { this.provider = provider; }   // Eskiz/PlayMobile
  async send(telefon, matn) {
    return this.provider.send(telefon, matn);     // biznes logika provayderni bilmaydi
  }
}

// Eskiz provayder (Misol 2 ni o'raydi)
const eskizProvider = { send: (tel, matn) => eskizSms(tel, matn) };
export const smsService = new SmsService(eskizProvider);
// Kelajakda: new SmsService(playMobileProvider) — biznes kod o'zgarmaydi (2.13)

Misol 4 — OTP yuborish endpoint (rate limiting bilan — 2.7)

js
import { otpYarat, otpHash } from "../utils/otp.js";
import { smsService } from "../services/sms.js";
import { redis } from "../config/redis.js";       // (5.21)

export const sendOtp = async (req, res, next) => {
  try {
    const { telefon } = req.body;                  // (validatsiya — 5.9)
    if (!/^\+998\d{9}$/.test(telefon)) {
      return res.status(400).json({ error: "Telefon noto'g'ri (+998XXXXXXXXX)" });
    }

    // RATE LIMITING: 1 daqiqada 1 marta (SMS bombing — 2.7, 14)
    const oxirgi = await redis.get(`otp:cooldown:${telefon}`);
    if (oxirgi) {
      return res.status(429).json({ error: "Iltimos, 1 daqiqa kuting" });   // 429 (5.7)
    }

    // Kod yarat, hash, Redis'ga saqla (TTL — 2.6)
    const kod = otpYarat();                        // (2.3)
    await redis.set(`otp:${telefon}`, otpHash(kod), "EX", 120);   // 2 daqiqa (2.4)
    await redis.set(`otp:cooldown:${telefon}`, "1", "EX", 60);    // 1 daqiqa cooldown
    await redis.set(`otp:attempts:${telefon}`, "0", "EX", 120);   // urinishlar (2.7)

    // SMS yubor 2.9-bob — kodni LOG QILMANG (14, 2.12)
    await smsService.send(telefon, `Tasdiqlash kodi: ${kod}`);

    res.json({ message: "Kod yuborildi", expiresIn: 120 });
    //  kodni JAVOBDA qaytarmang (14)! (faqat dev'da test uchun mumkin)
  } catch (err) { next(err); }
};

Misol 5 — OTP tekshirish endpoint (2.11, brute-force himoyasi)

js
import jwt from "jsonwebtoken";

export const verifyOtp = async (req, res, next) => {
  try {
    const { telefon, kod } = req.body;             // (5.9)
    const saqlanganHash = await redis.get(`otp:${telefon}`);
    if (!saqlanganHash) {
      return res.status(400).json({ error: "Kod muddati tugagan yoki yuborilmagan" });
    }

    // BRUTE-FORCE: urinishlarni cheklash (2.7, 14)
    const urinish = Number(await redis.get(`otp:attempts:${telefon}`)) || 0;
    if (urinish >= 5) {
      await redis.del(`otp:${telefon}`);           // kodni bekor (2.7)
      return res.status(429).json({ error: "Juda ko'p urinish. Yangi kod so'rang" });
    }

    // Tekshirish (2.5)
    if (otpHash(kod) !== saqlanganHash) {
      await redis.incr(`otp:attempts:${telefon}`); // urinishni oshir
      return res.status(400).json({ error: "Kod noto'g'ri" });
    }

    // TO'G'RI — kodni darrov bekor qiling (bir martalik — 2.1)
    await redis.del(`otp:${telefon}`);
    await redis.del(`otp:attempts:${telefon}`);

    // Foydalanuvchini top yoki yarat (login/ro'yxat birga — 2.11)
    let user = await User.findOne({ telefon });
    if (!user) {
      user = await User.create({ telefon, tasdiqlangan: true });   // yangi (ro'yxat)
    }
    // Token ber (5.15, 5.16)
    const accessToken = jwt.sign({ sub: user.id, rol: user.rol }, config.jwt.secret, { expiresIn: "15m" });
    res.json({ accessToken, user: { id: user.id, telefon: user.telefon } });
  } catch (err) { next(err); }
};

Misol 6 — DB bilan OTP (Redis'siz — 2.6)

js
// Redis bo'lmasa, DB'da (6, 2.6)
const otpSchema = new mongoose.Schema({
  telefon: { type: String, required: true, index: true },
  kodHash: { type: String, required: true },
  expiresAt: { type: Date, required: true },
  urinishlar: { type: Number, default: 0 },
});
// TTL index — MongoDB muddati o'tganini avtomatik o'chiradi (2.6)
otpSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });

// Yuborish
const kod = otpYarat();
await Otp.findOneAndUpdate(
  { telefon },
  { kodHash: otpHash(kod), expiresAt: new Date(Date.now() + 120000), urinishlar: 0 },
  { upsert: true }                                 // bor bo'lsa yangila, yo'q bo'lsa yarat
);
await smsService.send(telefon, `Kod: ${kod}`);

Misol 7 — Express rate limiter (qo'shimcha himoya — 2.7, 5.20)

js
import rateLimit from "express-rate-limit";

// OTP endpoint'iga IP bo'yicha ham chegara (2.7, 5.20)
export const otpLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,                        // 1 soat
  max: 10,                                          // IP'dan soatiga 10 OTP so'rovi
  message: { error: "Juda ko'p so'rov. Keyinroq urinib ko'ring" },
});

app.post("/api/auth/send-otp", otpLimiter, sendOtp);   // IP + telefon (Misol 4) — ikki qatlam

Misol 8 — Play Mobile provayder (2.10)

js
import axios from "axios";

// Play Mobile — Basic Auth (2.10)
export async function playMobileSms(telefon, matn) {
  const auth = Buffer.from(`${config.playmobile.login}:${config.playmobile.parol}`).toString("base64");
  await axios.post("https://send.smsxabar.uz/broker-api/send",
    {
      messages: [{
        recipient: telefon.replace("+", ""),
        "message-id": crypto.randomUUID(),         // noyob ID (5.3)
        sms: { originator: "3700", content: { text: matn } },
      }],
    },
    { headers: { Authorization: `Basic ${auth}` } }
  );
}
// Bir xil interfeys (send) — SmsService'da almashtirish oson (2.13, Misol 3)

5. To'g'ri va noto'g'ri holatlar

1) Math.random bilan kod

js
//  bashorat qilinadi (14, 2.3)
const kod = Math.floor(Math.random() * 900000) + 100000;

//  crypto
const kod = crypto.randomInt(100000, 1000000);

2) Rate limiting'siz OTP

text
 cheklovsiz  SMS bombing (pul!) + brute-force (14, 2.7)
 SMS so'rovi (telefon/IP) + tekshirish urinishi cheklangan

3) Kodni javobda/log'da qaytarish

js
//  kod javobda  SMS keraksiz, xavfsiz emas (14, 2.12)
res.json({ message: "Yuborildi", kod });

//  faqat "yuborildi"
res.json({ message: "Yuborildi" });

4) Ishlatilgan kodni bekor qilmaslik

text
 kod ishlatilgach ham amal qiladi (qayta ishlatiladi — 2.1)
 to'g'ri kiritilgach darrov o'chiring (redis.del — Misol 5)

5) Provayder kalitlarini kodda saqlash

js
//  email/parol kodda (14, 5.8)
const token = await login("admin@x.uz", "parol");

//  .env
const token = await login(config.eskiz.email, config.eskiz.password);

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — SMS kelmaydi

Sababi: telefon formati noto'g'ri (+ bilan/'siz), shablon tasdiqlanmagan 2.8-bob, token muddati tugagan 2.9-bob, balans yo'q. Yechimi: format (998XXXXXXXXX); provayderda shablonni tasdiqlat; token yangilash; balans.

Xato 2 — 401 Eskiz'dan

Sababi: token muddati tugagan/noto'g'ri 2.9-bob. Yechimi: tokenni qayta ol (Misol 2 — 401'da yangilash); email/parol to'g'rimi.

Xato 3 — OTP doim "noto'g'ri"

Sababi: hash mos emas (saqlashda/tekshirishda farqli), yoki muddati tugagan. Yechimi: bir xil hash funksiyasi; TTL tekshiring; kodni trim qiling (bo'shliq).

Xato 4 — SMS bombing (katta hisob keldi)

Sababi: rate limiting yo'q (2.7, 14). Yechimi: cooldown (telefon) + IP limit (Misol 4, 7).

Xato 5 — Kod muddatidan keyin ham ishlaydi

Sababi: muddat tekshirilmagan (DB — 2.6). Yechimi: Redis TTL (avtomatik) yoki expiresAt tekshiring (Misol 6).


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • crypto 5.3-bob: xavfsiz kod yaratish, hash.
  • Axios/HTTP (2.18-JS, 5.5): provayder API so'rovi.
  • Auth (5.15, 5.16): OTP tasdiqlangach token.
  • Env 5.8-bob: provayder kalitlari.
  • Validatsiya 5.9-bob: telefon formati.
  • Rate limiting 5.20-bob: SMS bombing/brute-force.
  • Redis 5.21-bob: OTP saqlash (TTL).
  • DB (6): foydalanuvchi (telefon).
  • Telegram bot 5.14-bob: botda OTP/telefon tasdiqlash.
  • SOLID 9.2-bob: provayder abstraksiyasi.

8. Eng yaxshi amaliyotlar (best practices)

  • Kod crypto.randomInt (Math.random emas — 2.3, 14); 6 xonali; 2-3 daqiqa muddat 2.4-bob.
  • Kodni hash qilib saqlang (ochiq emas — 2.5); Redis TTL bilan 2.6-bob.
  • Rate limiting MAJBURIY — SMS so'rovi (telefon+IP) + tekshirish urinishi (2.7, 14).
  • Ishlatilgan kodni darrov bekor (bir martalik — 2.1); urinish tugaganda bekor 2.7-bob.
  • Kodni javobda/log'da qaytarmang (14, 2.12).
  • Provayder kalitlari .env'da (14, 5.8).
  • Telefon validatsiyasi (format — 5.9).
  • Provayder abstraksiyasi (almashtirish oson — 2.13, 9).
  • Shablonni provayderda tasdiqlat 2.8-bob; token muddatini boshqar 2.9-bob.
  • OTP + auth birga (login/ro'yxat — 2.11).

9. Amaliy loyiha: "Telefon + OTP Autentifikatsiya Tizimi"

OTP va SMS integratsiyasini professional, xavfsiz darajada mustahkamlash.

Maqsad

Eskiz (yoki Play Mobile) bilan telefon raqami orqali OTP autentifikatsiya tizimini qurish: kod yuborish, tekshirish, rate limiting, muddat va token berish.

Talablar (requirements)

  1. OTP yaratish: crypto bilan 6 xonali; hash qilib saqlash (Redis/DB TTL — Misol 1, 6, 2.3, 2.5).
  2. SMS provayder: Eskiz integratsiyasi (token + yuborish); abstraksiya (Misol 2, 3, 2.9, 2.13).
  3. send-otp endpoint: telefon validatsiya; kod yuborish; cooldown rate limit (Misol 4, 2.7).
  4. verify-otp endpoint: kod tekshirish; brute-force limit; ishlatilgach bekor (Misol 5, 2.7).
  5. Auth birlashma: kod to'g'ri foydalanuvchi bor/yo'q login/ro'yxat + token (Misol 5, 2.11, 5.15).
  6. Rate limiting: telefon cooldown + IP limit (Misol 4, 7, 2.7, 14).
  7. Muddat: 2 daqiqa; muddatdan keyin yaroqsiz (Redis TTL — 2.4, 2.6).
  8. Xavfsizlik: kod hash; javobda/log'da qaytarmaslik; kalitlar .env (2.12, 14).
  9. (Bonus) Play Mobile: ikkinchi provayder; abstraksiya orqali almashtirish (Misol 8, 2.13).
  10. (Bonus) Qayta yuborish: "Kodni qayta yuborish" (cooldown'dan keyin).

Maslahatlar (hint)

  • Kod: crypto.randomInt(100000, 1000000) 2.3-bob.
  • Redis: set(otp:tel, hash, "EX", 120) (TTL — 2.6).
  • Cooldown: alohida otp:cooldown:tel kaliti 2.7-bob.
  • Brute-force: otp:attempts:tel hisoblagich 2.7-bob.
  • Eskiz: login token (keshla) SMS; 401'da token yangilash (Misol 2).
  • Kodni JAVOBDA qaytarmang (14, 3-xato).
  • Provayderni interfeys orqali (2.13, SOLID).

"Tayyor" mezonlari (acceptance criteria)

  • send-otp kod yuboradi (SMS keladi); telefon validatsiya.
  • verify-otp kodni tekshiradi; to'g'ri token.
  • Kod crypto bilan, hash qilib saqlanadi.
  • Muddat (2 daqiqa) ishlaydi; muddatdan keyin yaroqsiz.
  • Rate limiting: SMS so'rovi (cooldown) + brute-force urinishi.
  • Ishlatilgan kod darrov bekor qilinadi.
  • Login/ro'yxat birlashgan (telefon orqali).
  • Kod javobda/log'da yo'q; kalitlar .env'da.
  • (Bonus) Provayder abstraksiyasi / Play Mobile.

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda O'zbekistonda juda keng tarqalgan amaliy mavzuni — OTP va SMS integratsiyasini — chuqur o'rgandik:

  • OTP — bir martalik, qisqa muddatli kod (telefon egaligini isbotlaydi — 2.1); oqim (yarat yubor tekshir — 2.2).
  • Kod crypto bilan 2.3-bob; uzunlik/muddat (6 raqam, 2-3min — 2.4); hash qilib saqlang 2.5-bob; Redis TTL 2.6-bob.
  • Rate limiting MAJBURIY (SMS bombing + brute-force — 2.7, 14) — eng muhim qism.
  • SMS provayder 2.8-bobEskiz (token-based — 2.9), Play Mobile 2.10-bob; provayder abstraksiyasi 2.13-bob.
  • OTP + auth birga (parolsiz login/ro'yxat — 2.11); xavfsizlik (kod hash, javobda qaytarmaslik, kalitlar .env — 2.12, 14).

Keyingi bob — 5.19-bob: Email — Nodemailer. SMS'ni bildik; endi ikkinchi muhim aloqa kanalini — email yuborish (Nodemailer bilan) — o'rganamiz: SMTP, tasdiqlash xati, parolni tiklash, HTML shablon, ilova fayl, va email OTP. Email — ro'yxatdan o'tish tasdiqlash, bildirishnoma, hisobotlar uchun zarur.


Foydalanilgan rasmiy/ishonchli manbalar

  • notify.eskiz.uz — Eskiz SMS API (auth/login, message/sms/send); playmobile.uz — Play Mobile API
  • nodejs.org — crypto.randomInt; OTP best practices (expiry, rate limiting, hashing)
  • DEV/Medium — OTP verification with Node.js (rate limiting, brute-force himoyasi)

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
5.18-bob: OTP va SMS integratsiyasi (Eskiz / Play Mobile) — Wisar