Authentication is a critical aspect of modern web applications, and NestJS makes it simpler to implement robust authentication mechanisms using Passport. Whether you're building a REST API or a traditional web application, integrating authentication into your project is essential for securing user data and managing access control.
Passport is a middleware for Node.js that simplifies the implementation of various authentication strategies. From simple username/password to OAuth, it provides a wide array of options to fit various use cases. When used with NestJS, it allows for clean and organized authentication practices consistent with the framework's philosophy.
To start, ensure you have a NestJS project set up. If you haven't set up one yet, simply create a new Nest application:
$ npm i -g @nestjs/cli $ nest new nest-auth-example $ cd nest-auth-example
Next, install the necessary packages for Passport and JWT (JSON Web Tokens):
$ npm install @nestjs/passport passport passport-local @nestjs/jwt passport-jwt bcrypt
Here's a quick rundown of what each package does:
@nestjs/passport
: A NestJS module for integrating Passport.passport
: The core Passport library.passport-local
: This strategy allows username/password authentication.@nestjs/jwt
: JWT module for generating tokens.passport-jwt
: This strategy provides authentication using JWT.bcrypt
: A library to hash passwords for a secure authentication process.Before we dive into authentication, we'll need to set up a user model that our authentication strategies will use. Create a user module by running:
$ nest generate module users $ nest generate service users
Create a simple user entity, such as:
// src/users/user.entity.ts import { Exclude } from 'class-transformer'; export class User { id: number; username: string; @Exclude() password: string; }
In the users.service.ts
, implement basic features to manage users:
// src/users/users.service.ts import { Injectable } from '@nestjs/common'; import { User } from './user.entity'; @Injectable() export class UsersService { private readonly users: User[] = []; async create(username: string, password: string): Promise<User> { const user = new User(); user.id = this.users.length + 1; user.username = username; user.password = await this.hashPassword(password); this.users.push(user); return user; } async findOne(username: string): Promise<User | undefined> { return this.users.find(user => user.username === username); } private async hashPassword(password: string): Promise<string> { return bcrypt.hash(password, 10); } }
Here, we have basic logic to create a user and find one based on the username.
Next, integrate the Passport Local strategy to handle user login:
// src/auth/local.strategy.ts import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy } from 'passport-local'; import { UsersService } from '../users/users.service'; import { User } from '../users/user.entity'; @Injectable() export class LocalStrategy extends PassportStrategy(Strategy) { constructor(private readonly usersService: UsersService) { super({ usernameField: 'username' }); } async validate(username: string, password: string): Promise<User> { const user = await this.usersService.findOne(username); if (!user || !(await bcrypt.compare(password, user.password))) { throw new UnauthorizedException(); } return user; } }
Now, let's create JWT strategies for generating tokens:
$ nest generate module auth
// src/auth/jwt.strategy.ts import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { UsersService } from '../users/users.service'; import { User } from '../users/user.entity'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor(private readonly usersService: UsersService) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: 'your_secret_key', // Change this to your actual secret }); } async validate(payload: any): Promise<User> { return this.usersService.findOne(payload.username); } }
auth.service.ts
to generate the JWT:// src/auth/auth.service.ts import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { UsersService } from '../users/users.service'; @Injectable() export class AuthService { constructor(private usersService: UsersService, private jwtService: JwtService) {} async login(username: string): Promise<{ access_token: string }> { const user = await this.usersService.findOne(username); const payload = { username: user.username, sub: user.id }; return { access_token: this.jwtService.sign(payload), }; } }
Lastly, create an authentication controller to handle the incoming requests:
// src/auth/auth.controller.ts import { Controller, Post, Request, UseGuards } from '@nestjs/common'; import { AuthService } from './auth.service'; import { LocalAuthGuard } from './local-auth.guard'; // You need to create this guard @Controller('auth') export class AuthController { constructor(private authService: AuthService) {} @UseGuards(LocalAuthGuard) @Post('login') async login(@Request() req) { return this.authService.login(req.user); } }
To protect your routes, you can use guards. For example, to use the JWT strategy:
// src/auth/jwt-auth.guard.ts import { Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; @Injectable() export class JwtAuthGuard extends AuthGuard('jwt') {}
Now, you can secure any route by applying @UseGuards(JwtAuthGuard)
.
With the authentication flow set up, you can test it via Postman or any other API client:
Register a User: Make a POST request to your user registration endpoint (not implemented in detail here but can be derived from UsersService
).
Login: Send a POST request to /auth/login
with the username and password.
Access Protected Route: Use the received JWT token as a Bearer token to access protected routes.
This setup provides you with a solid foundation for incorporating authentication into your NestJS application. Feel free to enhance this system with additional features such as refresh tokens, social logins, etc. The power of NestJS combined with Passport's flexibility opens numerous avenues for building secure and scalable applications.
10/12/2024 | NestJS
10/12/2024 | NestJS
10/12/2024 | NestJS
10/12/2024 | NestJS
10/12/2024 | NestJS
10/12/2024 | NestJS
10/12/2024 | NestJS
10/12/2024 | NestJS