PassportJS — asoslar va strategiyalar

~25 daq · NestJS · Autentifikatsiya

Muammodan boshlaylik

Foydalanuvchi kirish uchun email va parol yubordi. Siz tekshirasiz — to'g'ri. Keyingi so'rovda esa kim ekanini qaerdan bilasiz? HTTP — stateless, har so'rov mustaqil. Server oldingi so'rovni "eslamaydi".

Bundan tashqari, keyinroq Google orqali kirish qo'shish kerak bo'ldi. Keyin Facebook. Keyin telefon raqami bilan. Har biri uchun alohida mantiq — kod takrorlanadi, xatolar ko'payadi.

PassportJS bu ikki muammoni hal qiladi: kim ekanini tekshirish mexanizmi (strategiya) va NestJS bilan ulanish (integratsiya).


PassportJS nima?

Passport — Node.js uchun autentifikatsiya middleware kutubxonasi. U 500+ "strategiya" ni qo'llab-quvvatlaydi: email/parol, JWT, Google, GitHub, Facebook va boshqalar.

Hayotiy o'xshatma: aeroportdagi pasport nazorati. Bir xil nazorat punkti turli pasportlarni tekshiradi — O'zbekiston, Rossiya, AQSh. Qaysi pasport bo'lishidan qat'i nazar, nazorat bir xil: "bu hujjat haqiqiymi? Bu odam kim?" PassportJS ham shunday — bir xil interfeys, turli tekshiruv usullari.


O'rnatish

npm install @nestjs/passport passport passport-local
npm install -D @types/passport-local

Autentifikatsiya uchun alohida modul yaratamiz:

src/
└── auth/
    ├── auth.module.ts
    ├── auth.service.ts
    ├── auth.controller.ts
    ├── strategies/
    │   └── local.strategy.ts    ← email/parol strategiyasi
    └── guards/
        └── local-auth.guard.ts  ← strategiyani ishlatuvchi guard

Local Strategiya — email va parol

// src/auth/strategies/local.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-local';
import { AuthService } from '../auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super({
      usernameField: 'email', // standart 'username' o'rniga 'email'
    });
  }

  // Passport bu metoddan foydalanuvchi topilsa qaytaradi,
  // topilmasa UnauthorizedException tashlaydi
  async validate(email: string, password: string) {
    const user = await this.authService.validateUser(email, password);
    if (!user) {
      throw new UnauthorizedException('Email yoki parol noto\'g\'ri');
    }
    return user; // ← req.user ga qo'yiladi
  }
}

PassportStrategy(Strategy) — bu NestJS va Passport o'rtasidagi ko'prik. validate metodi Passport tomonidan chaqiriladi — siz faqat ichini yozasiz.


Auth Service — foydalanuvchini tekshirish

// src/auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
  constructor(private usersService: UsersService) {}

  async validateUser(email: string, password: string) {
    const user = await this.usersService.findByEmail(email);
    if (!user) return null;

    const isMatch = await bcrypt.compare(password, user.password);
    if (!isMatch) return null;

    // Parolni olib tashlash — req.user da parol bo'lmasin
    const { password: _, ...result } = user.toObject();
    return result;
  }
}

{ password: _, ...result } — destructuring bilan parolni olib tashlaymiz. _ — bu qiymat kerak emas degani (konvensiya).


Local Guard — strategiyani ishlatish

// src/auth/guards/local-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}
// 'local' — LocalStrategy da register qilingan nom

Auth Module — hammasini birlashtirish

// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { LocalStrategy } from './strategies/local.strategy';
import { UsersModule } from '../users/users.module';

@Module({
  imports: [
    PassportModule,
    UsersModule, // ← UsersService ishlatish uchun
  ],
  providers: [AuthService, LocalStrategy],
  controllers: [AuthController],
  exports: [AuthService],
})
export class AuthModule {}

Controller — login endpoint

// src/auth/auth.controller.ts
import { Controller, Post, UseGuards, Req } from '@nestjs/common';
import { LocalAuthGuard } from './guards/local-auth.guard';

@Controller('auth')
export class AuthController {
  // POST /auth/login
  @Post('login')
  @UseGuards(LocalAuthGuard) // ← Passport email/parol tekshiradi
  login(@Req() req) {
    // LocalStrategy validate() muvaffaqiyatli bo'lsa
    // req.user = validate() dan qaytgan ob'ekt
    return req.user;
  }
}

So'rov oqimi:

POST /auth/login  { email, password }
       │
       ▼
LocalAuthGuard ishga tushadi
       │
       ▼
LocalStrategy.validate(email, password)
       │
       ▼
AuthService.validateUser(email, password)
       │  ✓ to'g'ri → user ob'ekti
       ▼
req.user = user
       │
       ▼
Controller.login(req) → req.user qaytariladi

UsersService ga findByEmail qo'shish

// src/users/users.service.ts
async findByEmail(email: string) {
  // select('+password') — schema da select: false bo'lgan parolni olish
  return this.userModel
    .findOne({ email, deletedAt: null })
    .select('+password')
    .exec();
}

Strategiyalarni tushunish — muhim nuqta

Passport ning kuchi — bir xil interfeys, har xil strategiyalar:

LocalStrategy    →  email + parol tekshiradi
JwtStrategy      →  JWT token tekshiradi
GoogleStrategy   →  Google OAuth tekshiradi
GithubStrategy   →  GitHub OAuth tekshiradi

Har strategiya:

  1. So'rovdan kerakli ma'lumotni oladi (email/parol, token, code...)
  2. validate() metodida tekshiradi
  3. Muvaffaqiyatli bo'lsa req.user ga qo'yadi

Keyingi darsda JWT strategiyasini qo'shamiz — login da token beramiz, keyin har so'rovda shu token tekshiriladi.


Tez-tez uchraydigan xatolar

usernameField unutish

// ❌ Standart — Passport 'username' maydonini qidiradi
super();

// ✅ Email ishlatayotgan bo'lsangiz
super({ usernameField: 'email' });

UsersModule ni export qilmaslik

// ❌ AuthModule da UsersService ko'rinmaydi
@Module({
  providers: [UsersService], // exports yo'q
})
export class UsersModule {}

// ✅
@Module({
  providers: [UsersService],
  exports: [UsersService], // ← AuthModule ishlatishi uchun
})
export class UsersModule {}

req.user bo'sh kelishi

Agar validate() null qaytarsa yoki xato tashlasa — Passport 401 Unauthorized qaytaradi, controller ga yetib bormaydi. Bu to'g'ri xatti-harakat.


Xulosa

PassportJS — autentifikatsiya strategiyalarini standart interfeys orqali boshqarish. Har strategiya validate() metodini implement qiladi.

Eslab qoling:

  • PassportStrategy(Strategy) — NestJS uchun ko'prik
  • validate() — Passport chaqiradi, siz mantiq yozasiz
  • AuthGuard('nom') — strategiyani faollashtiradi
  • req.user — validate() dan qaytgan ob'ekt
  • usernameField: 'email' — standart username o'rniga

Keyingi dars

26-dars: JWT Authentication — Access Token