Passport is a popular authentication middleware for Node.js that provides various authentication strategies, including JWT (JSON Web Token) authentication. NestJS is a powerful framework for building scalable and maintainable server-side applications with TypeScript. In this article, we will explore how to implement JWT authentication using Passport in a NestJS application.
Prerequisites
Before we begin, make sure you have the following prerequisites installed:
Node.js and npm
Nest CLI
MongoDB
Setting Up the NestJS Project
Let’s start by creating a new NestJS project. Open your terminal and run the following command:
nest new nest-jwt-auth-example Navigate into the project directory:
cd nest-jwt-auth-example Installing Dependencies
To implement JWT authentication using Passport and Mongoose, we need to install the necessary packages:
npm install @nestjs/jwt @nestjs/passport passport passport-jwt mongoose @nestjs/mongoose Setting Up Mongoose
First, let’s set up Mongoose to connect to your MongoDB database. Open the app.module.ts file and configure the MongooseModule:
import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; import { AppController } from './app.controller'; import { AppService } from './app.service'; @Module({ imports: [ MongooseModule.forRoot('mongodb://127.0.0.1:27017/nest-jwt-auth', { useNewUrlParser: true, useUnifiedTopology: true, }), ], controllers: [AppController], providers: [AppService], }) export class AppModule {} Replace the connection URL with your MongoDB connection string.
Creating User Schema
Let’s create a user schema using Mongoose. Create a new file named user.schema.ts in a folder named user inside the src directory:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { ObjectId } from 'mongodb'; @Schema() export class User { @Prop({ required: true }) username: string; @Prop({ required: true }) password: string; } export type UserDocument = User & Document; export const UserSchema = SchemaFactory.createForClass(User); Creating User Module
Now, create a user module to manage user-related operations. Run the following command to generate a new module, service, and controller:
nest generate module user nest generate service user nest generate controller user In the user.service.ts file, implement methods for user registration and fetching users:
import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { User, UserDocument } from './user.schema'; @Injectable() export class UserService { constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {} async create(username: string, password: string): Promise<User> { const user = new this.userModel({ username, password }); return user.save(); } async findByUsername(username: string): Promise<User | null> { return this.userModel.findOne({ username }).exec(); } async findById(id: string): Promise<User | null> { return this.userModel.findById(id).exec(); } } Setting Up Passport and JWT Strategy
Next, let’s configure Passport and set up the JWT strategy. Open the auth.module.ts file and create the JWT strategy:
import { Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; import { PassportModule } from '@nestjs/passport'; import { JwtStrategy } from './jwt.strategy'; import { AuthService } from './auth.service'; import { UserModule } from '../user/user.module'; @Module({ imports: [ PassportModule, JwtModule.register({ secret: 'yourSecretKey', // Replace with your own secret key signOptions: { expiresIn: '1h' }, // Token expiration time }), UserModule, ], providers: [AuthService, JwtStrategy], exports: [JwtModule], }) export class AuthModule {} Implementing JWT Strategy
Create a file named jwt.strategy.ts in the auth folder and implement the JWT strategy:
import { Injectable, UnauthorizedException } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy, ExtractJwt } from 'passport-jwt'; import { UserService } from '../user/user.service'; import { User } from '../user/user.schema'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor(private userService: UserService) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: 'yourSecretKey', // Replace with your own secret key }); } async validate(payload: any): Promise<User> { const user = await this.userService.findById(payload.sub); if (!user) { throw new UnauthorizedException(); } return user; } } Implementing Auth Service
Create an auth folder inside the src directory. Inside the auth folder, create auth.service.ts:
import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { UserService } from '../user/user.service'; import { User } from '../user/user.schema'; @Injectable() export class AuthService { constructor( private userService: UserService, private jwtService: JwtService, ) {} async validateUser(username: string, password: string): Promise<User | null> { const user = await this.userService.findByUsername(username); if (user && user.password === password) { return user; } return null; } async login(user: User): Promise<{ accessToken: string }> { const payload = { sub: user._id }; return { accessToken: this.jwtService.sign(payload), }; } } Creating Auth Controller
Generate an auth controller by running the following command:
nest generate controller auth In the auth.controller.ts file, implement the authentication endpoints:
import { Controller, Post, Request, UseGuards } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { AuthService } from './auth.service'; @Controller('auth') export class AuthController { constructor(private authService: AuthService) {} @Post('login') async login(@Request() req): Promise<any> { const user = req.user; const token = await this.authService.login(user); return { token }; } @Post('register') async register(@Request() req): Promise<any> { const { username, password } = req.body; const user = await this.authService.register(username, password); return { user }; } } Protecting Routes with AuthGuard
To protect routes with JWT authentication, use the AuthGuard provided by Passport. Open the app.controller.ts file and add the UseGuards decorator:
import { Controller, Get, Request, UseGuards } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; @Controller() export class AppController { @Get('profile') @UseGuards(AuthGuard()) getProfile(@Request() req) { return req.user; } } Testing the Endpoints
Start your NestJS application:
npm run start Now you can use tools like curl, Postman, or any API testing tool to test the authentication endpoints. Here's how you can test the endpoints using curl:
Register a new user:
curl -X POST http://localhost:3000/auth/register -H "Content-Type: application/json" -d '{"username": "testuser", "password": "testpassword"}'Login with the registered user:
curl -X POST http://localhost:3000/auth/login -H "Content-Type: application/json" -d '{"username": "testuser", "password": "testpassword"}'Use the obtained token to access the protected route:
curl http://localhost:3000/profile -H "Authorization: Bearer "
Conclusion
In this article, we’ve learned how to implement JWT authentication using Passport in a NestJS application with a Mongoose database. We covered setting up Mongoose, creating a user schema, configuring Passport and JWT strategy, and protecting routes using the AuthGuard. This approach provides a secure and efficient way to handle user authentication and authorization in your NestJS application.
Here’s a link to a sample project repository for a more concrete demonstration of the concepts discussed in this article: Github Repo

Top comments (0)