Passport JWT Authentication in NestJS
Tags: nestjs, passportjs, jwt, node
Published: 2023-08-18 02:33:18

![Passport JWT Authentication in NestJS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sdi4kofha85mxubopf18.png)

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:

1. Node.js and npm

2. Nest CLI

3. MongoDB

### Setting Up the NestJS Project
Let’s start by creating a new NestJS project. Open your terminal and run the following command:

```bash
nest new nest-jwt-auth-example
```

### Navigate into the project directory:

```bash
cd nest-jwt-auth-example
```

### Installing Dependencies
To implement JWT authentication using Passport and Mongoose, we need to install the necessary packages:

```bash
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:

```ts
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:

```ts
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:

```bash
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:

```ts
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:

```ts
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:

```ts
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:

```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:

```bash
nest generate controller auth
```

In the auth.controller.ts file, implement the authentication endpoints:

```ts
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:

```ts
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:

```bash
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:

1. Register a new user:
curl -X POST http://localhost:3000/auth/register -H "Content-Type: application/json" -d '{"username": "testuser", "password": "testpassword"}'

2. Login with the registered user:
curl -X POST http://localhost:3000/auth/login -H "Content-Type: application/json" -d '{"username": "testuser", "password": "testpassword"}'

3. Use the obtained token to access the protected route:
curl http://localhost:3000/profile -H "Authorization: Bearer <your-token>"

### 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](https://github.com/vishnucprasad/nest_auth)