Mundarija (33)
- Bu darsda
- 1. Nega bu kerak?
- 2. Passport qanday ishlaydi
- 2.1 Strategiya — kimlikni aniqlash usuli
- 2.2 Nest'da uch qism
- 2.3 Paketsiz model
- 2.4 Passport sessiyasi va Nest
- 3. LocalStrategy — email va parol
- 3.1 Strategiya
- 3.2 Controller va modul
- 4. JwtStrategy va @nestjs/jwt
- 4.1 Token chiqarish
- 4.2 Tokenni tekshirish
- 4.3 Biz ishga tushirdik
- 5. Kanon o'lchovi: Passport vs o'z guard'imiz
- 5.1 Tajriba
- 5.2 401 javobi
- 5.3 Nega kanonda yo'q
- 6. Hujumchi nigohi
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): bir nechta strategiya
- 3-mashq (qiyin): Problem Details 401
- 9. Loyiha qadami: 28-qism auth fayllarini qaytarish
- 9.1 Reja
- 9.2 Paketlar va fayllar
- 9.3 middleware.ts — Nest shakli
- 9.4 Turlar, tokenlar, config
- 9.5 Tekshiruv
- 10. Real ishda
- Xulosa
- Manbalar
Passport NestJS'da: strategiya, validate() va AuthGuard
Qisqacha: Passport — Node.js'da kimlikni tekshirishning eng ko'p ishlatiladigan kutubxonasi. Har usul (email + parol, JWT, Google) — alohida strategiya. Nest'da strategiya
PassportStrategy(Strategy)dan meros oladi. Uningvalidate()metodi foydalanuvchini qaytaradi va natijareq.userga yoziladi. Marshrutni esa@UseGuards(AuthGuard("jwt"))himoyalaydi. Kanon o'lchovida Passport 28-qismdagi o'z kodimiz bilan bir xil tezlikda ishladi. Lekin 401 javobi Problem Details emas edi va 3 ta qo'shimcha paket kerak bo'ldi — shuning uchunvazifalar-apida Passport yo'q.
Bu darsda
- Passport strategiyasi nima ekanini va Nest uni qanday guard'ga aylantirishini tushuntirasiz.
LocalStrategy(email + parol) vaJwtStrategy(Bearer token) yozasiz,validate()nima qaytarishi kerakligini bilasiz.@nestjs/jwtbilan token chiqarasiz va soxta token nega 401 olishini haqiqiy so'rovlarda ko'rasiz.- Kanon o'lchovi bilan Passport'ni o'z guard'imiz bilan solishtirasiz.
vazifalar-apiga 28-qismning auth fayllarini qaytarib, ko'chishning birinchi qadamini qo'yasiz.
Oldin bilishingiz kerak: Guard'lar, Session-based auth, JWT anatomiyasi, Access va refresh token strategiyasi, Custom dekoratorlar.
Auth'ni ataylab oxirga qoldirdik. Sababi oddiy: endi butun loyiha — controller'lar, xizmatlar, Swagger, health, SSE, testlar — Nest'da ishlayapti. 28-qismda Express uchun yozgan autentifikatsiyamiz shu tayyor uyga ko'chadi. Uy tayyor bo'lgani uchun har qadamda faqat bitta narsa o'zgaradi: kim so'rayotganini aniqlash Nest'da qanday bo'ladi.
1. Nega bu kerak?
28-qismda "kim so'rayapti" savoliga ikki funksiya javob berardi. kimlik() cookie'dagi sessiyani tekshirib, req.sessiya ni qo'yardi. kirishKerak esa sessiya bo'lmasa 401 qaytarardi. Kod kichik va bizniki edi.
Endi istalgan Nest vakansiyasini oching. Talablarda deyarli doim bir qator bor: "Passport, JWT". Nest hujjatining autentifikatsiya bo'limi ham Passport bilan boshlanadi. Ya'ni boshqa jamoaning Nest loyihasiga kirsangiz, katta ehtimol bilan AuthGuard("jwt") ni ko'rasiz.
Shuning uchun bu darsda ikki ish qilamiz. Avval Passport'ni Nest'da to'liq o'rganamiz: o'z demo loyihamizda haqiqiy so'rovlar bilan. Keyin kanon o'lchovi bilan ko'ramiz: nega vazifalar-api da uni ishlatmadik. Bu "Passport yomon" degani emas. Bu — aniq loyiha uchun o'lchab qilingan tanlov.
O'xshatish: Passport — universal kalitlar to'plami. Har eshik (Google, parol, JWT) uchun tayyor kalit bor. Agar uyingizda bitta eshik bo'lsa va o'z kalitingiz allaqachon ishlab tursa, to'plam og'irlik qiladi. Ko'p eshikli binoda esa to'plam vaqtni tejaydi.
2. Passport qanday ishlaydi
2.1 Strategiya — kimlikni aniqlash usuli
Strategiya (strategy) — bitta savolga javob beradigan kichik obyekt: "shu so'rovda kim bor?". passport-local email va parolni tanadan o'qiydi. passport-jwt sarlavhadagi tokenni tekshiradi. passport-google-oauth20 Google'ga yo'naltiradi. Passport'ning o'zi esa faqat strategiyani chaqiradi va natijaga qarab "o'tkaz" yoki "401" deydi.
Passport ekotizimida yuzlab tayyor strategiya bor. Kutubxonaning asosiy paketi passport 0.7.0 — 2023-yil noyabrdan beri yangi versiya chiqmagan. Nest uchun o'rami — @nestjs/passport 12.0.0 (2026-08-27, Nest 12 bilan birga chiqqan).
2.2 Nest'da uch qism
Nest'da Passport uch bo'lakdan yig'iladi:
| Bo'lak | Vazifasi |
|---|---|
PassportStrategy(Strategy, "nom") |
strategiyani Nest provider'iga aylantiradi |
validate(...) |
sizning kodingiz: foydalanuvchini toping yoki rad eting |
AuthGuard("nom") |
marshrutga qo'yiladigan guard |
Bog'lanish nom orqali: strategiya "jwt" nomi bilan ro'yxatdan o'tadi, guard AuthGuard("jwt") shu nomni chaqiradi. Nom berilmasa — strategiyaning o'z nomi olinadi (passport-local uchun "local", passport-jwt uchun "jwt").
2.3 Paketsiz model
G'oyani kichik JavaScript modelida ko'ramiz. Bu blok brauzerda ham ishlaydi. Haqiqiy Passport murakkabroq (sessiya, yo'naltirish, bir nechta strategiya), lekin yadrosi shu:
class Passport {
#strategiyalar = new Map();
use(nom, strategiya) {
this.#strategiyalar.set(nom, strategiya);
}
async authenticate(nom, req) {
const s = this.#strategiyalar.get(nom);
if (!s) {
throw new Error(`Unknown authentication strategy "${nom}"`);
}
const user = await s.validate(req);
if (!user) return 401;
req.user = user; // validate() natijasi
return 200;
}
}
const passport = new Passport();
passport.use("local", {
validate(req) {
const { email, parol } = req.body ?? {};
if (email === "aziz@example.com" && parol === "Bahor-2026") {
return { id: 1, email };
}
return null;
},
});
const a = { body: { email: "aziz@example.com", parol: "xato" } };
const b = {
body: { email: "aziz@example.com", parol: "Bahor-2026" },
};
console.log(await passport.authenticate("local", a), a.user);
console.log(await passport.authenticate("local", b), b.user);
try {
await passport.authenticate("jwt", {});
} catch (e) {
console.log(e.message);
}Konsolda:
401 undefined
200 { id: 1, email: 'aziz@example.com' }
Unknown authentication strategy "jwt"Uch qatorga qarang. validate() null qaytardi — 401, req.user yo'q. Obyekt qaytardi — 200 va obyekt req.user ga yozildi. Ro'yxatda yo'q nom — xato. Oxirgi xabar haqiqiy Passport'niki bilan aynan bir xil, uni "Ko'p uchraydigan xatolar" bo'limida ko'ramiz. Modelda soddalashtirgan joyimiz: parol ochiq solishtirilyapti — haqiqiy kodda hash va timingSafeEqual (parolni xavfsiz saqlash).
Tekshirib ko'ring:
validate()foydalanuvchi o'rnigatrueqaytarsa nima bo'ladi?
Javob
So'rov o'tadi (200), lekin req.user ga true yoziladi. Keyingi kod req.user.id ni o'qisa — undefined. Passport faqat "falsy yoki yo'q" ni tekshiradi. Shuning uchun validate() doim kerakli maydonlari bor obyekt qaytarishi kerak.
2.4 Passport sessiyasi va Nest
Passport Express dunyosida tug'ilgan. U yerda u odatda express-session bilan birga ishlaydi: kirgandan keyin foydalanuvchini sessiyaga yozadi (serializeUser), keyingi so'rovda o'qiydi (deserializeUser). Eski Express darsliklarida shu ikki funksiyani ko'p uchratasiz.
Nest'da bu sukut bo'yicha yo'q. @nestjs/passport strategiyani sessiyasiz chaqiradi: har so'rov o'z tokenini yoki parolini olib keladi, Passport uni tekshiradi va unutadi. Bu JWT uchun to'g'ri, chunki token o'zi "sessiya".
Sukutni modul darajasida o'zgartirish mumkin: PassportModule.register({ defaultStrategy: "jwt" }) — shunda AuthGuard() nom bermasdan ham "jwt" ni chaqiradi. session: true ham bor, lekin u express-session ni talab qiladi. 28-qismdagi server sessiyamiz esa o'zimizniki: sessiyalar jadvali, hash'langan token, __Host-sessiya cookie. Uni Passport'ning sessiya mexanizmiga bog'lash shart emas — buni kanon tajribasida ko'ramiz.
3. LocalStrategy — email va parol
3.1 Strategiya
O'z demo loyihamizda (nest new, NestJS 12.2.0) to'rt paket o'rnatdik: @nestjs/passport@12.0.0, passport@0.7.0, passport-local@1.0.0, @types/passport-local@1.0.38. Foydalanuvchilar — xotiradagi kichik ro'yxat (FoydalanuvchilarService.tekshir(email, parol) — scrypt hash bilan).
// local.strategy.ts — email + parol bilan kirish (passport-local)
import { Injectable, UnauthorizedException } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { Strategy } from "passport-local";
import { FoydalanuvchilarService } from "../foydalanuvchilar.js";
import type { Foydalanuvchi } from "../foydalanuvchilar.js";
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private readonly foyd: FoydalanuvchilarService) {
// sukut: "username" va "password" maydonlari — bizda boshqacha
super({ usernameField: "email", passwordField: "parol" });
}
// Passport tanadan email va parolni olib, shu metodni chaqiradi.
// Qaytgan qiymat — req.user ga yoziladi
validate(email: string, parol: string): Foydalanuvchi {
const f = this.foyd.tekshir(email, parol);
if (f === null) throw new UnauthorizedException();
return f;
}
}Qatorma-qator:
PassportStrategy(Strategy)— bu funksiya mixin: upassport-localningStrategyklassini olib, Nest provider'i bo'la oladigan yangi klass qaytaradi. Biz undan meros olamiz.super({...})— strategiya sozlamalari.passport-localsukut bo'yichausernamevapasswordmaydonlarini qidiradi. Bizemailvaparoldeymiz.validate(email, parol)— argumentlar strategiyaga bog'liq. Local'da — ikki satr, JWT'da — token ichidagi obyekt.UnauthorizedException— Nest'ning tayyor 401 xatosi.
Demodagi importlar .js bilan yozilgan — nest new shabloni va Nest hujjati shunday. vazifalar-api da esa .ts (Nest CLI va loyiha tuzilmasi).
3.2 Controller va modul
@Controller("auth")
export class AuthController {
constructor(private readonly jwt: JwtService) {}
@Post("kirish")
@HttpCode(200)
@UseGuards(AuthGuard("local"))
async kirish(@Req() req: Request): Promise<{ access: string }> {
const f = req.user as Foydalanuvchi; // LocalStrategy.validate()
const access = await this.jwt.signAsync(
{ sub: f.id, rol: f.rol });
return { access };
}
@Get("men")
@UseGuards(AuthGuard("jwt"))
men(@Req() req: Request): object {
return { user: req.user }; // JwtStrategy.validate()
}
}kirish metodi ichida parol tekshiruvi yo'q. Uni guard qildi. Metodga kelganda req.user allaqachon tayyor. @HttpCode(200) — POST sukut bo'yicha 201 qaytaradi (Controller'lar), kirish esa hech narsa "yaratmaydi".
req.user as Foydalanuvchi — Passport req.user ga tur bermaydi (Express.User — bo'sh interfeys). Kattaroq loyihada @Foydalanuvchi() kabi param dekorator yoziladi (Custom dekoratorlar).
Modul hammasini ulaydi:
@Module({
imports: [
PassportModule,
JwtModule.register({
secret: JWT_SIRI,
signOptions: { expiresIn: "15m", algorithm: "HS256" },
}),
],
controllers: [AuthController],
providers: [FoydalanuvchilarService, LocalStrategy, JwtStrategy],
})
export class AuthModule {}E'tibor bering: strategiyalar providers da. Nest ularni yaratganda constructor ichidagi super() strategiyani Passport'ga ro'yxatdan o'tkazadi. Provider bo'lmasa — strategiya ham yo'q.
4. JwtStrategy va @nestjs/jwt
4.1 Token chiqarish
@nestjs/jwt 12.0.3 — jsonwebtoken kutubxonasining Nest o'rami. JwtModule.register() sirni va sukut sozlamalarni oladi, JwtService esa signAsync() va verifyAsync() beradi. 15 daqiqalik access token — access va refresh strategiyasi darsidagi muddat.
Demoda sir koddagi satr. Haqiqiy loyihada u ConfigService dan keladi va kamida 32 tasodifiy bayt bo'ladi (@nestjs/config). Buning uchun register() emas, registerAsync() ishlatiladi — u dinamik modul darsidagi useFactory + inject naqshining o'zi:
JwtModule.registerAsync({
inject: [ConfigService],
useFactory: (c: ConfigService) => ({
secret: c.getOrThrow<string>("JWT_SIRI"),
signOptions: { expiresIn: "15m", algorithm: "HS256" },
}),
}),getOrThrow — sir berilmagan bo'lsa ilova ishga tushmaydi. Bu "sukut bo'yicha xavfsiz" tamoyili (xavfsizlik tamoyillari): sirsiz ishlab ketgan server hamma tokenni bo'sh satr bilan imzolashi mumkin edi.
4.2 Tokenni tekshirish
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, "jwt") {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false, // exp o'tgan — 401
secretOrKey: JWT_SIRI,
algorithms: ["HS256"], // faqat shu algoritm (alg:none yo'q)
});
}
// imzo va exp tekshirilgach chaqiriladi: payload — ishonchli
validate(p: Tolov): { id: number; rol: Rol } {
return { id: p.sub, rol: p.rol };
}
}Bu yerda validate() ga token emas, uning ochilgan va tekshirilgan payload'i keladi. Imzo noto'g'ri bo'lsa yoki exp o'tgan bo'lsa, validate() umuman chaqirilmaydi — passport-jwt o'zi 401 beradi.
ExtractJwt — tokenni qayerdan olishni aytadi: sarlavhadan, cookie'dan, query'dan. Keyingi darsda cookie variantini yozamiz.
algorithms: ["HS256"] ni unutmang. JWT anatomiyasi darsida alg: none va algoritm almashtirish hujumlarini ko'rgansiz. Ruxsat ro'yxati (allowlist) bo'lsa, kutubxona boshqa algoritmli tokenni ochmaydi.
4.3 Biz ishga tushirdik
Demo serverni listen(0) bilan ochib, fetch bilan yetti so'rov yubordik (Node 24.21.0, NestJS 12.2.0, @nestjs/passport 12.0.0, passport-jwt 4.0.1). Token har safar boshqacha (iat vaqtga bog'liq) — uni qisqartirdik:
1) noto'g'ri parol → 401 {"message":"Unauthorized","statusCode":401}
2) tana bo'sh → 401 {"message":"Unauthorized","statusCode":401}
3) to'g'ri parol → 200 {"access":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ…"}
sarlavha: {"alg":"HS256","typ":"JWT"}
payload: {"sub":1,"rol":"foydalanuvchi","iat":…,"exp":… (+900 s)}
4) tokensiz → 401 {"message":"Unauthorized","statusCode":401}
5) token bilan → 200 {"user":{"id":1,"rol":"foydalanuvchi"}}
6) rol admin'ga o'zgartirilgan → 401 {"message":"Unauthorized","statusCode":401}
7) alg: none → 401 {"message":"Unauthorized","statusCode":401}Chiqishni o'qiymiz:
- 1 va 2 —
LocalStrategy. Tana bo'sh bo'lsapassport-localvalidate()ni chaqirmaydi ham — o'zi rad etadi. - 3 — payload'da
exp − iat = 900soniya, ya'ni 15 daqiqa. Token ichida parol yo'q, faqatsubvarol. - 5 —
req.userendiJwtStrategy.validate()qaytargan obyekt. - 6 — payload'dagi
rolniadminga almashtirib, eski imzoni qoldirdik. Imzo mos kelmadi — 401. - 7 —
alg: none, imzosiz token.algorithmsro'yxatida yo'q — 401.
To'rtta 401 javobning tanasi bir xil: {"message":"Unauthorized"}. Bu xavfsizlik uchun yaxshi (sababini oshkor qilmaydi). Lekin bu Problem Details formati emas va WWW-Authenticate sarlavhasi ham yo'q. Bu farqqa hali qaytamiz.
sequenceDiagram
participant M as Mijoz
participant G as AuthGuard("jwt")
participant P as Passport
participant S as JwtStrategy
participant C as Controller
M->>G: GET /auth/men (Bearer)
G->>P: authenticate("jwt")
P->>S: imzo va exp tekshiruvi
alt token yaroqli
S->>P: validate(payload)
P->>G: req.user
G->>C: men(req)
else yaroqsiz
P->>G: xato
G->>M: 401
endDiagrammada guard o'zi hech narsa tekshirmayapti. U faqat Passport'ni chaqiradi, Passport — strategiyani. Nest'ning so'rov hayot sikli bo'yicha guard pipe va handler'dan oldin turadi, shuning uchun noto'g'ri token controller'gacha yetmaydi.
Tekshirib ko'ring: 6-so'rovda hujumchi faqat payload'ni o'zgartirdi. Nega server buni sezdi, axir payload shifrlanmagan?
Javob
Imzo sarlavha + payload ustidan hisoblanadi. Payload bitta harfga o'zgarsa ham, HMAC boshqacha chiqadi va eski imzo mos kelmaydi. JWT ma'lumotni yashirmaydi (base64url — shifr emas), lekin o'zgartirilganini aniqlaydi. Shuning uchun payload'ga sir yozilmaydi, lekin unga ishonsa bo'ladi — imzo tekshirilgandan keyin.
5. Kanon o'lchovi: Passport vs o'z guard'imiz
5.1 Tajriba
Kanon 28-qismdagi sessiyani Passport strategiyasi sifatida ham yozdi (passport-custom 1.2.1 — o'z validate(req) funksiyangiz bilan strategiya). Keyin uch varianti bir xil marshrutda o'lchadi: kanondagi kimlik middleware + KirishGuard, Passport strategiyasi va JWT (bazasiz tekshiruv). 32 parallel ulanish, 3 soniya, ikki aylanish.
- Kanon (sessiya)100 %
- Passport (sessiya)102 %
- JWT HS256153 %
Manba: IlmHamroh kanon o'lchovi, 2026-10-31: Node 24.21.0, NestJS 12.2.0, @nestjs/passport 12.0.0, @nestjs/jwt 12.0.3, Postgres 18; ikki aylanish o'rtachasi (2330 / 2383 / 3567 req/s); bitta mashina, bir vaqtda, mashina band
Grafikdan ikki xulosa:
- Passport bilan o'z guard'imiz orasida farq yo'q: 2 % — o'lchov shovqini ichida. Ikkalasi ham har so'rovda Postgres'dan sessiyani o'qiydi. Vaqtni baza oladi, kutubxona emas.
- JWT ~1.5 baravar ko'p so'rov o'tkazdi, chunki bazaga bormaydi. Bu narxi bor tezlik — keyingi darsda o'lchaymiz.
5.2 401 javobi
Kanon chiqishidan, cookie'siz so'rov (tana qisqartirilgan):
kanon 401 application/problem+json; charset=utf-8 www-authenticate=Cookie realm="vazifalar" tana={"type":"https://vazifalar-api.example/xatolar/kirish-kerak","title":"Kirish kerak","status":401,"detail":"Avval tizimga
passport 401 application/json; charset=utf-8 www-authenticate=— tana={"message":"Unauthorized","statusCode":401}25-qismdan beri vazifalar-api ning hamma xatosi Problem Details (RFC 9457 darsi). Frontend detail ni ko'rsatadi, kod bo'yicha qaror qiladi. Passport'ning 401 tanasi bu shartnomani buzadi. Tuzatsa bo'ladi: AuthGuard ning handleRequest() metodini qayta yozib, o'z xatoingizni tashlaysiz. Lekin bu — yana kod.
5.3 Nega kanonda yo'q
| Mezon | Passport | Kanon (kimlik + KirishGuard) |
|---|---|---|
| Tezlik | teng (~2.4k req/s) | teng (~2.3k req/s) |
| Paketlar | +3 | 0 |
| 401 tanasi | {"message":…} |
Problem Details |
WWW-Authenticate |
yo'q | bor (RFC 9110) |
Passport'ning asl kuchi — ko'p tashqi provayder uchun tayyor strategiyalar. Bizda bitta tashqi provayder bor (Google, OIDC), u ham 28-qismda openid-client bilan to'g'ridan yozilgan. Ya'ni Passport bizga yangi imkoniyat bermaydi, faqat qatlam qo'shadi. Kanon qarori (docs/AUTH-QAROR.md): Passport ishlatilmaydi; o'nlab tashqi provayder kerak bo'lsa — qayta ko'riladi.
Tekshirib ko'ring: Passport va kanon guard'i bir xil tezlikda ekan. Unda nega kanon "+3 paket" ni jiddiy kamchilik deb hisobladi?
Javob
Har paket — kuzatish kerak bo'lgan kod: yangilanishlar, zaifliklar, tranzitiv bog'liqliklar (ta'minot zanjiri xavfsizligi). Foyda bo'lmasa, narx qoladi. Passport bu loyihada yangi imkoniyat bermaydi (strategiyalar kerak emas), lekin 401 formatini tuzatish uchun qo'shimcha kod talab qiladi. Tezlik teng bo'lganda hal qiluvchi narsa — soddalik va javob shartnomasi.
Maslahat: Bu qaror sizning keyingi ish joyingizga taalluqli emas. Jamoada Passport bo'lsa — yuqoridagi uch bo'lakni bilganingiz yetarli. Yangi loyiha boshlasangiz — o'lchang va tanlang.
6. Hujumchi nigohi
Bu darsdagi kodni hujumchi qayerdan buzishga urinadi:
- Parol tanlash.
AuthGuard("local")o'zi tezlikni cheklamaydi. HimoyasizPOST /auth/kirish— credential stuffing uchun ochiq eshik. Himoya — tezlik cheklovi (@nestjs/throttler va kanonCheklovGuard). - Foydalanuvchini sanash. "Bunday email yo'q" va "parol noto'g'ri" — ikki xil xabar bo'lsa, hujumchi qaysi email ro'yxatda borligini biladi. Demodagi
validate()ikkalasida bir xilUnauthorizedExceptiontashlaydi. - Algoritmni almashtirish.
algorithmsberilmasa, ba'zi kutubxona versiyalari tokendagialgga ishonadi. Biz 7-so'rovda ko'rdik: ro'yxat bor —alg: noneo'tmadi. - Kuchsiz sir. HS256 siri qisqa bo'lsa, tokenni offline tanlab topish mumkin (JWT anatomiyasi). Sir — kamida 32 tasodifiy bayt, kodda emas,
.envda. validate()da bazaga qaramaslik. JwtStrategy faqat payload'ni qaytaradi. Foydalanuvchi bloklangan bo'lsa ham token 15 daqiqa ishlaydi. Bu masalani keyingi darsda o'lchaymiz.
7. Ko'p uchraydigan xatolar
Strategiya providers ga qo'shilmagan, lekin AuthGuard("jwt") ishlatilgan. Demoda shu holatni yasadik:
ERROR [ExceptionsHandler] Error: Unknown authentication strategy "jwt"
at attempt (…\node_modules\passport\lib\middleware\authenticate.js:193:39)
GET /auth/men → 500 {"statusCode":500,"message":"Internal server error"}Tarjimasi: "Noma'lum autentifikatsiya strategiyasi: jwt". Bu 401 emas, 500 — dasturchi xatosi. Ilova ishga tushadi va xato faqat birinchi so'rovda chiqadi. Davosi — strategiyani modulning providers iga qo'shing va nomni tekshiring ("jwt" va "JWT" — ikki xil nom). Iz qatorini qisqartirdik.
| Belgi | Sabab | Davosi |
|---|---|---|
| To'g'ri parolga ham 401 | usernameField berilmagan, tanada email |
super({ usernameField: "email" }) |
| Kirish 201 qaytaradi | POST sukuti | @HttpCode(200) |
req.user.id — undefined |
validate() true qaytargan |
foydalanuvchi obyektini qaytaring |
| Muddati o'tgan token o'tadi | ignoreExpiration: true |
false (sukut) |
500 Unknown authentication strategy |
strategiya provider emas | providers: [JwtStrategy] |
8. Mashqlar
Mashqlar kurs/mashqlar/30/55-passport/ da.
1-mashq (oson): bo'shliqlarni to'ldiring
- Strategiyani Nest provider'iga aylantiradigan funksiya —
(Strategy) validate()qaytargan qiymat yoziladigan joy —req.- Marshrutni JWT bilan himoyalash —
@UseGuards(AuthGuard("")) passport-localsukut bo'yicha qidiradigan parol maydoni —- Kanon o'lchovida Passport kanon guard'iga nisbatan necha foiz tezlik ko'rsatdi? —
Yechim
- PassportStrategy — mixin: strategiya klassini olib, Nest provider'i bo'la oladigan klass qaytaradi.
- user —
req.user; Passport univalidate()natijasidan qo'yadi. - jwt —
passport-jwtstrategiyasining sukut nomi. - password — shuning uchun bizda
passwordField: "parol". - 102 — farq o'lchov shovqini ichida: vaqtni baza oladi.
2-mashq (o'rta): bir nechta strategiya
Haqiqiy AuthGuard massiv qabul qiladi: AuthGuard(["jwt", "apikalit"]) — birinchi muvaffaqiyatli strategiya yutadi. Darsdagi modelga authenticateBiri(nomlar, req) metodini qo'shing: nomlarni tartib bilan sinasin, birinchi 200 da to'xtasin, hammasi 401 bo'lsa — 401. Ishora: oddiy for...of va mavjud authenticate().
Yechim
class Passport {
#strategiyalar = new Map();
use(nom, s) {
this.#strategiyalar.set(nom, s);
}
async authenticate(nom, req) {
const s = this.#strategiyalar.get(nom);
if (!s) {
throw new Error(`Unknown authentication strategy "${nom}"`);
}
const user = await s.validate(req);
if (!user) return 401;
req.user = user;
return 200;
}
async authenticateBiri(nomlar, req) {
for (const nom of nomlar) {
if ((await this.authenticate(nom, req)) === 200) return 200;
}
return 401;
}
}
const p = new Passport();
p.use("jwt", {
validate: (req) => (req.token === "t-1" ? { id: 1 } : null),
});
p.use("apikalit", {
validate: (req) =>
(req.kalit === "vz_1" ? { id: 1, kalit: true } : null),
});
const a = { kalit: "vz_1" };
console.log(await p.authenticateBiri(["jwt", "apikalit"], a), a.user);
console.log(await p.authenticateBiri(["jwt", "apikalit"], {}));Konsolda:
200 { id: 1, kalit: true }
401Birinchi so'rovda jwt 401 berdi, lekin to'xtamadik — apikalit o'tdi. Kanondagi kimlik() ham shunday ikki yo'lni biladi (Bearer API kaliti yoki cookie sessiyasi), faqat strategiyalarsiz, bitta funksiyada.
3-mashq (qiyin): Problem Details 401
Passport'ning 401 tanasi {"message":"Unauthorized"}. Nest'da buni AuthGuard ning handleRequest(err, user) metodini qayta yozib tuzatishadi: user yo'q bo'lsa — o'z xatoingizni tashlaysiz. Modelda handleRequest(err, user, yol) funksiyasini yozing: err bo'lsa — uni qaytarsin, user bo'lmasa — { status: 401, sarlavhalar, tana } obyektini qaytarsin, bunda WWW-Authenticate: Bearer realm="vazifalar" va Problem Details tanasi (type, title, status, instance) bo'lsin; user bo'lsa — user ning o'zini. Ishora: obyekt va JSON.
Yechim
const TUR = "https://vazifalar-api.example/xatolar/kirish-kerak";
function handleRequest(err, user, yol) {
if (err) return err;
if (!user) {
return {
status: 401,
sarlavhalar: {
"Content-Type": "application/problem+json",
"WWW-Authenticate": 'Bearer realm="vazifalar"',
},
tana: JSON.stringify({
type: TUR, title: "Kirish kerak", status: 401, instance: yol,
}),
};
}
return user;
}
console.log(handleRequest(null, { id: 1 }, "/v1/auth/men"));
const j = handleRequest(null, false, "/v1/auth/men");
console.log(j.status, j.sarlavhalar["WWW-Authenticate"]);
console.log(j.tana);Konsolda:
{ id: 1 }
401 Bearer realm="vazifalar"
{"type":"https://vazifalar-api.example/xatolar/kirish-kerak","title":"Kirish kerak","status":401,"instance":"/v1/auth/men"}Nest'da xuddi shu mantiq class JwtGuard extends AuthGuard("jwt") ichida yoziladi va obyekt o'rniga xato tashlanadi — uni Problem Details filtri javobga aylantiradi (Exception filter'lar). Ishlaydi, lekin endi Passport ustiga o'z kodimiz qo'shildi — kanon nega to'g'ridan o'z guard'ini tanlaganining yana bir sababi.
9. Loyiha qadami: 28-qism auth fayllarini qaytarish
9.1 Reja
Ko'chish yetti darsga bo'linadi. Har darsda bitta qatlam qo'shiladi, commit esa oxirida — Autentifikatsiya II darsida:
flowchart LR
A["Kim: kimlik, egasi"] --> B["Sessiya cookie"]
B --> C["Global guard"]
C --> D["Egalik va RLS"]
D --> E["Kirish yo'llari"]
E --> F["Tezlik, CAPTCHA"]
F --> G["Modul, testlar, commit"]Bugun — birinchi quti: "kim so'rayapti". Bu qatlam guard'lardan oldin, oddiy Express middleware sifatida ishlaydi. Sababini global guard darsida ko'ramiz.
9.2 Paketlar va fayllar
vazifalar-api papkasida (C:/Users/…/vazifalar-api):
npm i --save-exact @simplewebauthn/server@14.0.3 openid-client@6.8.8
npm i -D --save-exact @simplewebauthn/browser@14.0.0 \
oidc-provider@9.12.2 @types/oidc-provider@9.12.1 playwright@1.64.0package.json ga 28-qismdagi overrides bo'limini ham qaytaring (jose 6.2.12 va @peculiar/asn1-* 2.11.0 — tranzitiv paketlar). Kanon yangi bog'liqliklarning hammasini (tranzitivlari bilan 55 ta) 7 kunlik qoidaga tekshirdi: 7 kundan yangi paket — 0.
28-qism yakuniy commit'ingizdan o'zgarishsiz qaytadigan fayllar (Express'ga bog'liq emas — toza mantiq):
| Papka | Fayllar |
|---|---|
src/auth/ |
sessiya, parol, token, otp, totp, mfa, oidc, telegram, api-kalit, captcha, csrf, ombor, sxema, pochta, imzo-cookie, taqiqlangan-parollar |
src/umumiy/ |
cheklov, fon, semafor, sir, xavfsiz-url, kursor (HMAC imzoli), webhook (SSRF himoyasi) |
sql/, prisma/ |
109–122 va ularning 11 ta Prisma migratsiyasi, schema.prisma |
Bu fayllar qayerdan keladi? Nest CLI va loyiha tuzilmasi darsida ko'chishni 27-qism commit'idan yangi nest tarmog'ida boshlagandik; main esa 29-qism holatida ishlab turgan Express API bo'lib qoldi. Demak 28-qism commit'i main tarixida bor — uni git log main --oneline bilan topasiz. Siz nest tarmog'ida turib, kerakli fayllarni o'sha commit'dan olasiz. Masalan, 28-qism commit'i abc1234 bo'lsa: git checkout abc1234 -- src/auth/sessiya.ts. Bu buyruq tarmoqni almashtirmaydi — faqat bitta faylni o'sha commit'dagi holatiga keltiradi. Express'ga bog'langan marshrut.ts, mfa-marshrut.ts, admin.ts qaytmaydi — ularning o'rniga keyingi darslarda controller'lar yoziladi.
xizmat.ts ham qaytadi, bitta import o'zgaradi: MfaBogliqliklari turi Express routeridan (mfa-marshrut.ts) yangi kichik mfa-turlar.ts fayliga ko'chdi (28-qismdagi tur va MfaXatosi aynan).
9.3 middleware.ts — Nest shakli
28-qismdagi faylning ikki funksiyasidan biri qoladi, biri o'zgaradi. kimlik() — aynan. kirishKerak (401/403) — olib tashlandi: bu endi global guard'ning ishi. O'rniga egasiniAniqla() keldi — u faqat "kim" ni yozadi va hech kimni rad etmaydi. Kanon faylida qatorlar uzun edi — telefon uchun bo'ldik (izohlar qisqartirildi, kod ma'nosi aynan):
// middleware.ts — kim so'rayapti. kimlik() — cookie'dagi sessiyani
// tekshiradi va req.sessiya ni qo'yadi (bo'lmasa — jim o'tadi);
// 30-qism: kirishKerak (401/403) — KirishGuard'ga ko'chdi
// (qoriqchi.ts); bu yerda egasiniAniqla() — req.egasi + CLS.
import type { RequestHandler } from "express";
import { IlovaXatosi } from "../umumiy/xatolar.ts";
import { cookieNomi, cookieOl } from "./sessiya.ts";
import type { SessiyaOmbori } from "./sessiya.ts";
import type { ApiKalitlar } from "./api-kalit.ts";
export class KirilmaganXatosi extends IlovaXatosi {
constructor() {
// RFC 9110: 401 javobida WWW-Authenticate bo'lishi kerak. Cookie
// sessiyasi uchun standart sxema yo'q — o'zimizniki
super(401, "Avval tizimga kiring", {
kod: "kirish-kerak",
sarlavha: "Kirish kerak",
httpSarlavhalar: {
"WWW-Authenticate": 'Cookie realm="vazifalar"',
},
});
}
}
export function kimlik(
sessiyalar: SessiyaOmbori,
apiKalitlar?: ApiKalitlar,
): RequestHandler {
const nom = cookieNomi(sessiyalar.s);
return async (req, _res, next) => {
const auth = req.get("authorization");
if (auth !== undefined && apiKalitlar !== undefined) {
const m = /^Bearer (\S{1,200})$/.exec(auth);
const k = m?.[1] === undefined
? undefined
: await apiKalitlar.tekshir(m[1]);
if (k === undefined) {
throw new IlovaXatosi(401, "API kaliti yaroqsiz, " +
"bekor qilingan yoki muddati o'tgan", {
kod: "api-kalit-xato",
sarlavha: "Kalit yaroqsiz",
httpSarlavhalar: {
"WWW-Authenticate":
'Bearer realm="vazifalar", error="invalid_token"',
},
});
}
req.apiKalit = k;
next();
return;
}
const token = cookieOl(req.headers.cookie, nom);
if (token !== undefined) {
const s = await sessiyalar.tekshir(token);
if (s !== undefined) req.sessiya = s;
}
next();
};
}
export function egasiniAniqla(
devEgasi: number | undefined,
clsga: (egasi: number) => void,
): RequestHandler {
return (req, _res, next) => {
const e = req.apiKalit?.foydalanuvchiId
?? req.sessiya?.foydalanuvchi.id ?? devEgasi;
if (e !== undefined) {
req.egasi = e;
clsga(e);
}
next();
};
}Passport bilan solishtiring: kimlik() — bizning "strategiyamiz". U ikki usulni biladi (Bearer va cookie) va natijani req.user emas, aniq turli req.sessiya / req.apiKalit ga yozadi. egasiniAniqla() so'rov egasini nestjs-cls kontekstiga ham qo'yadi — xizmatlar uni o'sha yerdan oladi. DEV rejimda (xotira yoki fayl ombori, auth yo'q) egasi — config'dagi EGASI_ID, 25–27-qismlardagi xulq.
9.4 Turlar, tokenlar, config
src/express.d.ts—Requestga to'rt maydon:sessiya?,egasi?,apiKalit?,xomTana?(xizmatlararo HMAC uchun xom baytlar).src/tokenlar.ts— yangi DI tokenlari:AUTH,OMBOR_FABRIKASI,KURSOR_KALITI,ICHKI_SIR,SIRLARva CLS kalitiCLS_EGASI = "egasi".src/config.ts— 28-qismning sozlamalari Zod sxemasiga: sessiya muddatlari,COOKIE_SECURE,ILOVA_SIRI,AUTH_DATABASE_URL, Google, Telegram, WebAuthn, Turnstile (config validatsiyasi).src/bogliqliklar.module.ts—SIRLAR(ILOVA_SIRIdan HKDF kalitlari),KURSOR_KALITI,ICHKI_SIRprovider'lari; webhook xabarchisi SSRF himoyasi bilan.
9.5 Tekshiruv
Bu qadam hech narsani ulamaydi: fayllar bor, lekin ilova ularni hali chaqirmaydi. Shuning uchun tiplar toza bo'lishi kerak. Shu kesimni biz kanon fayllaridan yig'ib tekshirdik:
npx prisma generate
npx tsc --noEmit; echo "exit $?"✔ Generated Prisma Client (7.10.1) to .\src\generated\prisma in 1.40s
exit 0Xato chiqsa — eng ko'p uchraydigani TS2307 (fayl qaytarilmagan). Testlarni bu darsda yurgizmaymiz. Hali commit qilmang — commit butun auth ulangach, Autentifikatsiya II darsida.
10. Real ishda
- Nest loyihalarining ko'pida auth — Passport:
LocalStrategy+JwtStrategy+ globalJwtAuthGuard. Kodni o'qishda birinchi qidiradigan narsa —*.strategy.tsfayllar va ularningvalidate()lari. - Ko'p provayder (Google, GitHub, Apple, korporativ SAML) — Passport eng foydali joy. Har biri uchun tayyor strategiya bor.
- Kod review'da: "
validate()bazadan foydalanuvchini tekshiryaptimi yoki faqat payload'ni qaytaryaptimi?", "algorithmsberilganmi?", "sir qayerdan keladi?". - Intervyu: "Passport strategiyasi nima?", "
validate()qaytargan qiymat qayerga tushadi?", "AuthGuard('jwt')qanday ishlaydi?".
Xulosa
- Passport — kimlikni aniqlash usullari (strategiyalar) to'plami; Nest'da
PassportStrategy(Strategy, "nom")+validate()+AuthGuard("nom"). validate()qaytarganireq.userga tushadi;nullyoki xato — 401.JwtStrategydavalidate()ga tekshirilgan payload keladi;algorithmsruxsat ro'yxatini doim bering.- Kanon o'lchovi: Passport o'z guard'imiz bilan bir xil tezlikda, lekin +3 paket va Problem Details'siz 401 —
vazifalar-apida ishlatilmaydi. - Loyiha: 28-qism auth mantig'i qaytdi,
kimlik()vaegasiniAniqla()— Express middleware; tip toza, commit hali yo'q.
Keyingi dars: Access/refresh token va cookie NestJS'da — refresh rotation va HttpOnly cookie'ni Nest'da yozamiz, keyin "chiqish"dan keyin JWT qancha yashashini o'lchaymiz.
Manbalar
- NestJS hujjati — Passport (authentication) recipe: https://docs.nestjs.com/recipes/passport
- NestJS hujjati — Authentication: https://docs.nestjs.com/security/authentication
- Passport.js hujjati va strategiyalar ro'yxati: https://www.passportjs.org/
- passport-jwt (ExtractJwt, algorithms): https://github.com/mikenicholson/passport-jwt
- RFC 9110 — 401 va WWW-Authenticate: https://www.rfc-editor.org/rfc/rfc9110#name-401-unauthorized
- Kanon o'lchovi (IlmHamroh, 2026-10-31): Passport (passport-custom), JWT va sessiya tezligi, 401 javobi;
docs/AUTH-QAROR.md. Demo loyiha (LocalStrategy, JwtStrategy, xato strategiya) va loyihaning oraliqtsctekshiruvi — biz ishga tushirdik (2026-11-02, NestJS 12.2.0, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!