Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get JWT token from headers in controller

Please, help me to find how to optimize my code. I need to limit the data for logged user. To do that, I need to get UUID from JWT token from Request. But I don't like my approach because I have duplicates of code:

const jwt = request.headers.authorization.replace('Bearer ', '');
const json = this.jwtService.decode(jwt, { json: true }) as { uuid: string };

Any one know how I can optimize that?

Here is my controller's code.

import { Controller, Get, Put, Body, Param, UseGuards, Req } from '@nestjs/common';
import { SettingService } from '../services';
import { AuthGuard } from '@nestjs/passport';
import { ResultInterface } from '../interfaces';
import { Request } from 'express';
import { JwtService } from '@nestjs/jwt';

@Controller('settings')
export class SettingController {
  /**
   * @param service
   * @param jwtService
   */
  constructor(private readonly service: SettingService,
              private readonly jwtService: JwtService) {
  }

  @UseGuards(AuthGuard('jwt'))
  @Get()
  async findAll(@Req() request: Request): Promise<ResultInterface> {
    const jwt = request.headers.authorization.replace('Bearer ', '');
    const json = this.jwtService.decode(jwt, { json: true }) as { uuid: string };
    const data = await this.service.findAll(json.uuid);
    return { rows: data };
  }

  @UseGuards(AuthGuard('jwt'))
  @Get(':id')
  async findOne(@Param('id') id: number, @Req() request: Request): Promise<ResultInterface> {
    const jwt = request.headers.authorization.replace('Bearer ', '');
    const json = this.jwtService.decode(jwt, { json: true }) as { uuid: string };
    const data = await this.service.findOneById(id, json.uuid);
    return { row: data };
  }

  @UseGuards(AuthGuard('jwt'))
  @Put()
  update(@Body() data: any, @Req() request: Request): Promise<any> {
    const jwt = request.headers.authorization.replace('Bearer ', '');
    const json = this.jwtService.decode(jwt, { json: true }) as { uuid: string };
    return this.service.update(data, json.uuid);
  }
}
like image 270
Lilian Costaș Avatar asked Sep 07 '19 12:09

Lilian Costaș


People also ask

What is JWT token header?

The header typically consists of two parts: the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA. For example: { "alg": "HS256", "typ": "JWT" } Then, this JSON is Base64Url encoded to form the first part of the JWT.


2 Answers

In order to make your code more readable and transparent, you can create a @AuthUser() decorator and reuse it across all of your controllers. When you are using AuthGuard('jwt') you already are decoding the token and if you are using jwt.decode function again you double decode the token.

I attached below and example of user decorator and order controller function that returns all my orders.

user.decorator.ts

import { createParamDecorator } from '@nestjs/common';

export const AuthUser = createParamDecorator((data, req) => {
  return req.user;
});

order.controller.ts

 @ApiOperation({ title: 'Get my orders' })
 @Get('/me')
 @UseGuards(AuthGuard('jwt'))
 async findMyOrders(@AuthUser() user: any): Promise<Order[]> {
   return this.orderService.findbyUserId(user._id);
 }
like image 175
Dragos Lupei Avatar answered Oct 11 '22 01:10

Dragos Lupei


You could create a JWTUtil that does that for you... Maybe something like this?

@Injectable()
export class JWTUtil {
    constructor(private readonly jwtService: JWTService) {}

    decode(auth: string): {uuid: string}{
        const jwt = auth.replace('Bearer ', '');
        return this.jwtService.decode(jwt, { json: true }) as { uuid: string };
    }
}

And then use it like this:

@Controller('settings')
export class SettingController {
  constructor(
      private readonly jwtUtil: JWTUtil,
      private readonly service: SettingService,
      ) {}

  @Get()
  @UseGuards(AuthGuard('jwt'))
  async findAll(@Headers('Authorization') auth: string): Promise<ResultInterface> {
    const json = await this.jwtUtil.decode(auth);
    const data = await this.service.findAll(json.uuid);

    //....
  }
}

Also note that you can directly access the Authorization header from the controller. Instead of passing through the Request object.

like image 36
Caius Avatar answered Oct 11 '22 01:10

Caius