[NestJS] passport로 JWT 인증하기

jm4293·2024년 1월 3일
0

Passport

  • Passport는 Node.js용 인증 미들웨어 라이브러리
  • 다양한 인증 전략(예: 로컬, OAuth, JWT 등)을 쉽게 구현할 수 있게 한다
  • 사용자 인증을 처리하고, 인증 성공 시 사용자 정보를 요청 객체에 첨부한다

로그인 성공하면 accessToken, refrechToken 전달

  • auth.controller.ts
@Post('login')
async login(@Body() input: LoginUserDto, @Res() res: Response) {
  const { email, password } = input;

  const query = `SELECT * FROM user WHERE email = '${email}'`;
  const user = await this.userRepository.query(query);

  if (user.length === 0) {
    throw new HttpException('이메일 또는 비밀번호가 일치하지 않습니다.', HttpStatus.BAD_REQUEST);
  }

  const validPassword = await bcrypt.compare(password, user[0].password);

  if (!validPassword) {
    throw new HttpException('이메일 또는 비밀번호가 일치하지 않습니다.', HttpStatus.BAD_REQUEST);
  }

  this.authService.setRefreshToken({ user: user[0], res });

  const jwt = this.authService.getAccessToken({ user: user[0] });

  return res.status(200).json({
    status: 200,
    message: '로그인이 완료되었습니다.',
    result: {
      email: user[0].email,
      name: user[0].name,
      age: user[0].age,
      phone: user[0].phone,
      jwt,
    },
  });
}
  • auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(private readonly jwtService: JwtService) {}

  getAccessToken({ user }): string {
    return this.jwtService.sign(
      {
        email: user.email,
      },
      {
        secret: process.env.ACCESS_TOKEN_SECRET_KEY,
        expiresIn: '5s',
      },
    );
  }

  setRefreshToken({ user, res }) {
    const refreshToken = this.jwtService.sign(
      {
        email: user.email,
      },
      {
        secret: process.env.ACCESS_TOKEN_SECRET_KEY,
        expiresIn: '2w',
      },
    );

    // res.setHeader('Set-Cookie', `refreshToken=${refreshToken}`);

    res.cookie('refreshToken', refreshToken, {
      httpOnly: true,
      path: '/',
      maxAge: 60 * 60 * 24 * 14 * 1000, // 2주
      // secure: true, // HTTPS 사용 시 주석 해제
      // domain: 'localhost:3000', // 필요한 경우 도메인 설정
      // sameSite: 'None', // 다른 도메인 간 요청에 필요
    });
  }
}
  • 프론트엔드
export const loginJWT_api = async (data: FormInputs1) =>
  await axios
    .post("http://localhost:8080/auth/login", data, {
      withCredentials: true,
    })
    .then(res => res.data);

const login = useMutation({
  mutationKey: ["login"],
  mutationFn: (data: FormInputs1) => loginJWT_api(data),
  onSuccess: res => {
    alert("로그인이 완료되었습니다.");
    sessionStorage.setItem("accessToken", res.result.jwt);
    navigate("/board");
    reset1();
  },
  onError: (err: any) => {
    alert(err.response.data.message);
  },
});
  • 로그인 성공하면 refrechToken은 쿠키로 저장될 수 있게 res.cookie 설정함
    1. 응답 헤더에 Set-Cookie로 되어 정확하게 전달되는데 쿠키에 저장이 안되는 현상이 나타남
    2. 찾아본 결과 프론트쪽에도 설정을 해야한다.
    3. axios 호출에서 옵션에 withCredentials: true 을 설정을 해야 쿠키로 저장이된다.

테스트를 위해 accessToken 유효시간을 5초로 해보았다

  • board.controller.ts
  @UseGuards(AuthGuard('access'))
  @Get('/user')
  find(@Req() request: Request): Promise<Board> {
    return this.boardService.findOne(+request.query.idx);
  }
  • 테스트를 위해 게시판 상세정보를 불러오는 api에만 @UseGuards(AuthGuard('access')) 데코레이터 적용

  • 5초전에는 게시판 상세정보 api가 성공으로 날라오다가 5초 뒤엔 401로 accessToken이 만료되어 401로 날라온다

  • accessToken이 만료되었으니 refrechToken을 사용하여 accessToken을 재발급을 받아야한다.

  • 프론트엔드 axios 설정

import axios from "axios";

export const tokenAxios = axios.create();

tokenAxios.interceptors.request.use(
  config => {
    const token = sessionStorage.getItem("accessToken");
    if (token) {
      config.headers["Authorization"] = `Bearer ${token}`;
    }
    return config;
  },
  error => {
    return Promise.reject(error);
  }
);

tokenAxios.interceptors.response.use(
  response => {
    return response;
  },
  error => {
    const originalRequest = error.config;
    if (error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;

      axios
        .post("http://localhost:8080/auth/refresh", {}, { withCredentials: true })
        .then(res => {
          console.log("res111", res);
        });
    }
    return Promise.reject(error);
  }
);
  • error 코드가 401이라면 refrech api를 요청해 재발급을 받기를 윈했지만 현재는 service에 문제가 있어 재발급이 불가능하다.

내일

  • refrechToken을 이용하여 accessToken을 재발급받는 과정을 완료할 것이다.
profile
무언가를 만드는 것을 좋아합니다

0개의 댓글

관련 채용 정보