🔐 Authentication và Authorization trong NestJS – JWT & Guards nâng cao
🧭 Giới thiệu
Trong các ứng dụng backend hiện đại, Authentication (xác thực) và Authorization (phân quyền) là hai phần quan trọng nhất để bảo vệ dữ liệu người dùng.
NestJS hỗ trợ sẵn các công cụ mạnh mẽ như Passport, JWT và Guards, giúp bạn xây dựng hệ thống bảo mật chặt chẽ mà vẫn dễ mở rộng.
Bài viết này sẽ hướng dẫn chi tiết cách:
- Tạo cơ chế đăng nhập bằng JWT.
- Dùng Passport để xác thực người dùng.
- Tạo Guard tùy chỉnh để phân quyền linh hoạt.
- Tối ưu bảo mật với Refresh Token và Middleware.
🧩 Authentication và Authorization là gì?
Trước tiên, cần phân biệt rõ:
- Authentication: Xác thực danh tính người dùng (bạn là ai?).
- Authorization: Kiểm tra quyền truy cập (bạn được làm gì?).
Hai bước này luôn đi cùng nhau.
Ví dụ: Một người dùng đã đăng nhập (authenticated) có thể chỉ được xem dữ liệu, nhưng không được xóa (authorization).
⚙️ Thiết lập JWT trong NestJS
JWT (JSON Web Token) giúp xác thực người dùng mà không cần lưu session server-side.
1️⃣ Cài đặt package cần thiết
npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt
2️⃣ Tạo AuthModule
@Module({
imports: [
JwtModule.register({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '1h' },
}),
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
})
export class AuthModule {}
3️⃣ Tạo AuthService
@Injectable()
export class AuthService {
constructor(private jwtService: JwtService) {}
async login(user: any) {
const payload = { username: user.email, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
};
}
}
4️⃣ Cấu hình chiến lược Passport JWT
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET,
});
}
async validate(payload: any) {
return { userId: payload.sub, email: payload.username };
}
}
🧠 Sử dụng Guards để bảo vệ route
Guard trong NestJS giúp chặn hoặc cho phép request trước khi tới controller.
Ví dụ: JwtAuthGuard
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
Áp dụng vào route:
@UseGuards(JwtAuthGuard)
@Get('profile')
getProfile(@Request() req) {
return req.user;
}
Tạo Guard phân quyền (RoleGuard)
@Injectable()
export class RolesGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const user = request.user;
return user?.role === 'admin';
}
}
Sử dụng Guard kết hợp:
@UseGuards(JwtAuthGuard, RolesGuard)
@Get('admin')
getAdminPage() {
return 'Welcome admin!';
}
🔁 Refresh Token – Giữ đăng nhập an toàn
Để tránh việc người dùng bị đăng xuất mỗi khi token hết hạn, ta sử dụng refresh token.
Ví dụ:
- Khi đăng nhập, API trả về
access_token
vàrefresh_token
. access_token
có thời hạn ngắn (1h),refresh_token
có thời hạn dài hơn (7d).- Khi
access_token
hết hạn, người dùng gọi API refresh để lấy token mới.
✅ Ưu điểm của cơ chế bảo mật trong NestJS
Tính năng | Mô tả |
---|---|
Modular | Dễ mở rộng với nhiều chiến lược xác thực khác nhau. |
Secure | Sử dụng JWT giúp giảm phụ thuộc session. |
Flexible | Có thể tạo guard tùy chỉnh cho từng use case. |
Scalable | Dễ áp dụng cho microservices. |
⚠️ Các lỗi thường gặp
Lỗi | Cách khắc phục |
---|---|
Token không hợp lệ | Kiểm tra JWT_SECRET đồng nhất giữa sign và verify. |
Token hết hạn sớm | Điều chỉnh expiresIn phù hợp. |
Không inject được JwtService | Đảm bảo JwtModule được import đúng module. |
Guard không chạy | Kiểm tra decorator @UseGuards() có được áp dụng. |
🔗 Tham khảo thêm
🚀 Kết luận
Nhờ Passport, JWT và Guards, NestJS giúp bạn xây dựng hệ thống xác thực và phân quyền linh hoạt, mạnh mẽ mà vẫn giữ cấu trúc rõ ràng.
Hơn nữa, việc tách riêng từng phần giúp bạn dễ bảo trì, dễ mở rộng, và đảm bảo an toàn cho ứng dụng của mình.
Vì vậy, hãy bắt đầu chuẩn hóa cơ chế bảo mật ngay từ hôm nay.