Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to refresh token in Nestjs

Tags:

import { ExtractJwt, Strategy } from 'passport-jwt'; import { AuthService } from './auth.service'; import { PassportStrategy } from '@nestjs/passport'; import { Injectable, UnauthorizedException } from '@nestjs/common'; import { JwtPayload } from './model/jwt-payload.model';  @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) {   constructor(private readonly authService: AuthService) {     super({       jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),       secretOrKey: 'secretKey',     });   }    async validate(payload: JwtPayload) {     const user = await this.authService.validateUser(payload);     if (!user) {       throw new UnauthorizedException();     }     return true;   } } 

Token is extracted from the request by PassportStrategy. I don't know how to catch the error when the token expires or gets invalid. My purpose is if there is an error because the token expired, I need to refresh the token. Otherwise do something else.

like image 480
Sang Dang Avatar asked Nov 14 '18 08:11

Sang Dang


People also ask

How to use refresh token in nestjs?

To refresh the token, the user needs to call a separate endpoint, called /refresh. This time, the refresh token is taken from the cookies and sent to the API. If it is valid and not expired, the user receives the new access token. Thanks to that, there is no need to provide the username and password again.

What is token in Nestjs?

The token will be used to validate users that need to access protected routes. To achieve that we will be making use of some packages: npm install --save @nestjs/passport @nestjs/jwt passport-jwt npm install --save-dev @types/passport-jwt.

How does JWT refresh token work?

Refresh Tokens: It is a unique token that is used to obtain additional access tokens. This allows you to have short-lived access tokens without having to collect credentials every time one expires.

What is refresh token in node JS?

Refresh token: The refresh token is used to generate a new access token. Typically, if the access token has an expiration date, once it expires, the user would have to authenticate again to obtain an access token.


1 Answers

Refresh token implementation could be handled in canActivate method in custom auth guard.

If the access token is expired, the refresh token will be used to obtain a new access token. In that process, refresh token is updated too.

If both tokens aren't valid, cookies will be cleared.

@Injectable() export class CustomAuthGuard extends AuthGuard('jwt') {   private logger = new Logger(CustomAuthGuard.name);    constructor(     private readonly authService: AuthService,     private readonly userService: UserService,   ) {     super();   }    async canActivate(context: ExecutionContext): Promise<boolean> {     const request = context.switchToHttp().getRequest();     const response = context.switchToHttp().getResponse();      try {       const accessToken = ExtractJwt.fromExtractors([cookieExtractor])(request);       if (!accessToken)         throw new UnauthorizedException('Access token is not set');        const isValidAccessToken = this.authService.validateToken(accessToken);       if (isValidAccessToken) return this.activate(context);        const refreshToken = request.cookies[REFRESH_TOKEN_COOKIE_NAME];       if (!refreshToken)         throw new UnauthorizedException('Refresh token is not set');       const isValidRefreshToken = this.authService.validateToken(refreshToken);       if (!isValidRefreshToken)         throw new UnauthorizedException('Refresh token is not valid');        const user = await this.userService.getByRefreshToken(refreshToken);       const {         accessToken: newAccessToken,         refreshToken: newRefreshToken,       } = this.authService.createTokens(user.id);        await this.userService.updateRefreshToken(user.id, newRefreshToken);        request.cookies[ACCESS_TOKEN_COOKIE_NAME] = newAccessToken;       request.cookies[REFRESH_TOKEN_COOKIE_NAME] = newRefreshToken;        response.cookie(ACCESS_TOKEN_COOKIE_NAME, newAccessToken, COOKIE_OPTIONS);       response.cookie(         REFRESH_TOKEN_COOKIE_NAME,         newRefreshToken,         COOKIE_OPTIONS,       );        return this.activate(context);     } catch (err) {       this.logger.error(err.message);       response.clearCookie(ACCESS_TOKEN_COOKIE_NAME, COOKIE_OPTIONS);       response.clearCookie(REFRESH_TOKEN_COOKIE_NAME, COOKIE_OPTIONS);       return false;     }   }    async activate(context: ExecutionContext): Promise<boolean> {     return super.canActivate(context) as Promise<boolean>;   }    handleRequest(err, user) {     if (err || !user) {       throw new UnauthorizedException();     }      return user;   } } 

Attaching user to the request is done in validate method in JwtStrategy class, it will be called if the access token is valid

@Injectable() export class JwtStrategy extends PassportStrategy(Strategy) {   constructor(     readonly configService: ConfigService,     private readonly userService: UserService,   ) {     super({       jwtFromRequest: cookieExtractor,       ignoreExpiration: false,       secretOrKey: configService.get('jwt.secret'),     });   }    async validate({ id }): Promise<User> {     const user = await this.userService.get(id);     if (!user) {       throw new UnauthorizedException();     }      return user;   } } 

Example for custom cookie extractor

export const cookieExtractor = (request: Request): string | null => {   let token = null;   if (request && request.signedCookies) {     token = request.signedCookies[ACCESS_TOKEN_COOKIE_NAME];   }   return token; }; 
like image 194
Željko Šević Avatar answered Nov 01 '22 01:11

Željko Šević