In Part-1 we have done complete implementation on NestJS application authentication by creating the user access token. Now we are going to explore the implementation of the refresh token. We will continue to work on the sample app we have used in Part-1.
Refresh Token Flow:
- Refresh Token is a random string key that will be created along with the JWT access token and return to the valid client on successful logging in.
- Now for all subsequent requests will use the access token, but the access token is a short-lived token where as refresh token lives more time than the access token.
- On the expiration of the access token, the user instead of authenticating himself again passing his user name and password, the user can send the refresh token.
- The server on receiving a refresh token, first it validates against the storage(database, cache, etc).
- For a valid refresh token server will create a new access token and refresh token(like when authenticate using user name and password) return it to the user.
Create A Refresh Token:
Install 'rand-token' nodejs plugin to generate a random token which will be our refresh token.
Command To Install Random Token Generator:
npm install rand-token --save.
Now update our User model with new properties to store the refresh token and its expiration time in the database.src/users/users.ts:
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; @Entity() export class User{ @PrimaryGeneratedColumn() id:number; @Column({name:"username"}) userName: string; @Column() password: string; @Column() refreshtoken:string; @Column() refreshtokenexpires:string; }Define a method in 'UserService' to save the refresh token into the database.
src/users/users.service.ts:
async saveorupdateRefreshToke( refreshToken:string, id:string, refreshtokenexpires){ await this.userRepo.update(id,{refreshtoken:refreshToken, refreshtokenexpires}); }
- Updating the 'refreshToken' and 'refreshtokenexpires' value to the database based on the user id.
src/auth/auth.service.ts:(top of the file)
var randtoken = require('rand-token');Add a method for generating the refresh token in 'AuthService' as below
src/auth/auth.service.ts:
async generateRefreshToken(userId): Promise<string>{ var refreshToken = randtoken.generate(16); var expirydate =new Date(); expirydate.setDate(expirydate.getDate() + 6); await this.usersService.saveorupdateRefreshToke(refreshToken, userId, expirydate); return refreshToken }
- (Line: 2) Generating the refresh token.
- (Line: 4) Defining the expiration time of refresh token. Here in our sample defining 6days.
- (Line: 5) Invoking the database method in the UserService file for updating these refresh token and expiration time to the database.
src/auth/aut.service.ts:
async login(user:any){ const payload = {username: user.userName, sub: user.userId}; return{ accessToken : this.jwtService.sign(payload), refreshToken: await this.generateRefreshToken(user.userId) } }
- (Line: 5) Returns the refresh token.
Refresh Token Strategy Using passport-jwt Library:
In Part-1 we have created an access token validation strategy using the 'passport-jwt' library, now we are going to create a refresh token strategy using the 'passport-jwt' strategy.
src/auth/jwt.refreshtoken.strategy.cs:
import { PassportStrategy } from '@nestjs/passport'; import { Strategy, ExtractJwt } from 'passport-jwt'; import {Injectable, UnauthorizedException, Body} from '@nestjs/common'; import {UsersService} from '../users/users.service'; @Injectable() export class JwtRefreshTokenStrategy extends PassportStrategy(Strategy,"jwt-refreshtoken") { constructor(private userService:UsersService) { super({ jwtFromRequest: ExtractJwt.fromBodyField('accessToken'), ignoreExpiration: true, secretOrKey: 'My Secret Never let outsiders', passReqToCallback:true }); } async validate(req,payload: any) { var user = await this.userService.findOne(payload.username); if(!user){ throw new UnauthorizedException(); } if(req.body.refreshToken != (await user).refreshtoken){ throw new UnauthorizedException(); } if( new Date() > new Date((await user).refreshtokenexpires)){ throw new UnauthorizedException(); } return { userId: payload.sub, username: payload.username }; } }
- (Line: 7)The 'PassportStrategy' defined with type 'Strategy' that loads from 'passport-jwt'. The 'jwt-refreshtoken' is our name of the strategy we will use this name in AuthGauds to invoke the strategy.
- (Line: 10) The 'jwtFromRequest' accepts the access token. The reason we are passing the expired access token to the server along with the refresh token because to fetch the user info like userId, etc. The 'ExtractJwt.fromBodyField()' captures the access token from the payload body of the request.
- (Line: 11) The 'ignoreExpiration' was set to 'true' because we sending to the server expired access token so we are informing server no to check the expiry date of the access token.
- (Line: 12) The 'secretOrKey' property value must match with the value in JwtModule configured in AuthModule.
- (Line: 13) By default, the 'validate()' method in strategy only sends the payload of the decrypted access token. But we need to get a refresh token from the request body as well. So by setting 'passReqToCallback' to 'true' then the 'validate()' method can receive 'Request' object as input parameter.
- (Line: 19) Fetches the user data.
- (Line: 20-22) If no user data returns an unauthorized response.
- (Line: 23-25) Compares the refresh token received from the request body with the refresh token from the user table data. If they don't match returns an unauthorized response
- (Line: 26-27) Validate the refresh token expiration.
Now import the 'JwtRefreshTokenStrategy' into the provider's array of the AuthModule.
src/auth/auth.module.ts:
import { Module } from '@nestjs/common'; import {JwtRefreshTokenStrategy} from './jwt.refreshtoken.strategy'; // existing code hidden for display purpose @Module({ providers: [JwtRefreshTokenStrategy] }) export class AuthModule {}
Refresh Token Endpoint:
Le'ts define a refresh token endpoint that fetches the new access token to the user.
src/app.controller.ts:
@UseGuards(AuthGuard('jwt-refreshtoken')) @Post('auth/refreshtoken') async refreshToken(@Request() req){ return await this.authService.login(req.user); }
- The AuthGuard invoking our 'jwt-refreshtoken' strategy.
Support Me!
Buy Me A Coffee
PayPal Me
Wrapping Up:
Hopefully, I think this article delivered some useful information on the Jwt authentication in the NestJS application. I love to have your feedback, suggestions, and better techniques in the comment section below.
Comments
Post a Comment