In this article, we target to generate the jwt authentication and store it in the HttpOnly cookie for user authentication. Part-1 completely explains implementing user registration in the nestjs application.
src/users/users.service.ts:
Implement Logic To Validate User Credentials:
First, let's create a model to store the valid user.
src/models/current.user.ts:
export class CurrentUser { userId: number; firstName: string; lastName: string; email: string; }Now we have to implement the logic for the login endpoint that is to validate user email and password.
src/users/users.service.ts:
public async validateUserCredentials(email: string, password: string):Promise<CurrentUser> { let user = await this.user.findOne({ email: email }); if (user == null) { return null; } const isValidPassword = await bcrypt.compare(password, user.password); if (!isValidPassword) { return null; } let currentUser = new CurrentUser(); currentUser.userId = user.userId; currentUser.firstName = user.firstName; currentUser.lastName = user.lastName; currentUser.email = user.email; return currentUser; }
- Using email fetching user data from the database. Using 'bcrypt.compare' method validating the hash password. If credentials are valid then returning the user information.
Passport Library:
- Passport is the most popular node.js authentication library.
- The 'Passport' library integration involves 'Strategy'. The 'Strategy' is used for reading the user credentials and validating them by invoking the 'validation()' method to check for the valid user.
- After successfully validating the user, then the 'Passport' attaches the user information to the 'Request' object which will be used by the route handlers.
passport-local Library:
By extending the 'Passport' library there are so many different strategies that are available as separate plugins. The 'passport-local' library is one such strategy.
The 'passport-local' strategy to authenticate the user using 'username' and 'password'. These values will be read by the strategy from the body of the request. By default 'username' and 'password' parameter names should be the same as here we mentioned, if we pass the values with different names then the PassportStrategy unable to read the values. So to make use of the custom payload variable name we need to specify those names at the time of local PassportStrategy configuration using the options 'usernameField', 'passwordField'.
Install passport-local Npm Packages:
Command To Install passport-local Packages:
npm install --save @nestjs/passport passport passport-local
npm install --save-dev @types/passport-local
Create A passport-local Strategy:
First, let's import the 'PassportModule' into the 'UsersModule'.
src/users/users.module.ts:
import { PassportModule } from '@nestjs/passport'; // code hidden for display purpose @Module({ imports: [ PassportModule ] }) export class UsersModule {}Now let's create a model that will be used by the local passport strategy to hold the valid user information.
src/users/local.strategy.ts:
import { Injectable, UnauthorizedException } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy } from 'passport-local'; import { CurrentUser } from 'src/models/current.user'; import { UsersService } from 'src/users/users.service'; @Injectable() export class LocalStrategy extends PassportStrategy(Strategy, 'local') { constructor(private userService: UsersService) { super({ usernameField: 'email' }); } async validate(email: string, password: string): Promise<CurrentUser> { let user = await this.userService.validateUserCredentials(email, password); if (user == null) { throw new UnauthorizedException(); } return user; } }
- To create a strategy class must implement the 'PassportStrategy'. Here we specified our strategy name like 'local'.
- By default, the local passport strategy reads the fields like 'username' and 'password, from the request body. Here we override the 'usernameField' with our custom variable name like 'email'.
- The 'validate' method gets automatically executed.
src/users/users.modulet.ts:
import { LocalStrategy } from './local.strategy'; // code hidden for display purspose @Module({ providers: [LocalStrategy], }) export class UsersModule {}
Install NestJS JWT Npm Package:
Command To Install NestJS Jwt Package:
npm install --save @nestjs/jwt
Configure JwtModule:
We have to configure the 'JwtModule' in the 'UsersModule'.
src/users/users.module.ts:
import { JwtModule } from '@nestjs/jwt'; // code hidden for display purpose @Module({ imports: [ JwtModule.register({ secret: 'My random secret key never let others', signOptions: { expiresIn: '1h', }, }) ], }) export class UsersModule {}
- Here 'secret' key will be utilized as one of the ingredients in the generation JWT token.
- Here specified expiration time like '1hr.
Implement Logic To Generate JWT Token:
Using the jwt plugin that we installed, it is very easy to generate the JWT auth token.
src/users/users.service.ts:
import { JwtService } from '@nestjs/jwt'; import { CurrentUser } from 'src/models/current.user'; @Injectable() export class UsersService { constructor(@InjectRepository(User) private user: Repository<User>, private jwtService:JwtService) {} public async getJwtToken(user:CurrentUser): Promise<string>{ const payload = { ...user } return this.jwtService.signAsync(payload); } }
- Here generating the jwt token with some user's data as payload to it.
Create Login Endpoint To Generate Jwt HttpOnly Cookie:
Now let's create our user login endpoint.
src/users/users.controllers.cs:
import { Body, Controller, Get, Post, Req, Res, UseGuards, } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { Response } from 'express'; import { CurrentUser } from 'src/models/current.user'; import { UsersService } from './users.service'; @Controller('users') export class UsersController { constructor(private userService: UsersService) {} @Post('login') @UseGuards(AuthGuard('local')) async login(@Req() req, @Res({ passthrough: true }) res: Response) { const token = await this.userService.getJwtToken(req.user as CurrentUser); const secretData = { token, refreshToken: '', }; res.cookie('auth-cookie', secretData,{httpOnly:true,}); return {msg:'success'}; } }
- (Line: 20) The 'AuthGuard' takes the name of our password strategy as an input parameter(ex: 'local'). So this guard will trigger our password storage and fetches the valid user information based on the credentials. Finally, it adds the valid user information into the 'Req' object.
- (Line: 22) Generating the user jwt auth token.
- (Line: 24-27) Preparing secret data that needs to be stored in the cookie like jwt and refresh(refresh token generation will be explained in the next part of the series).
- (Lin: 29) Creating the HTTP-Only auth cookie. So the HttpOnly cookie only read by the server, can't be accessed by the javascript.
Video Session:
Support Me!
Buy Me A Coffee
PayPal Me
Wrapping Up:
Hopefully, I think this article delivered some useful information on generating HttpOnly Jwt Auth Cookie in the NestJS application. I love to have your feedback, suggestions, and better techniques in the comment section below.
Refer:
Source Code
NestJS JWT Auth Cookie Series | Part -1 | User Rgistration
NestJS JWT Auth Cookie Series | Part-3 | Refresh Token
NestJS JWT Auth Cookie Series | Part -1 | User Rgistration
NestJS JWT Auth Cookie Series | Part-3 | Refresh Token
Comments
Post a Comment