로그아웃 프로세스의 이해

김지우·2022년 12월 16일
0

6주차

목록 보기
6/6
post-thumbnail
post-custom-banner

JWT + Redis를 사용한 로그아웃 로직

클라이언트로 부터 accessToken , refresh Token을 둘 다 받습니다.

=> 로그아웃 API가 호출되면 JWT를 Redis에 저장됩니다.

=> Redis에 넣을 때 expiration time 을 JWT의 exiration time 과 current time 을 계산해서 저장됩니다.

=> 만료 시간이 지난 토큰이면 Redis에서 자동 삭제됩니다.

=> 클라이언트가 기존 토큰으로 요청시 JwtFilter에 있는 validate 과정에서 redis로 해당 accessToken 있는지 확인합니다.

=> Redis에 값이 있으면 요청을 거부, 없으면 요청을 승인합니다.

1. auth.resolver.ts 에 logout API 생성

  @UseGuards(GqlAuthRefreshGuard)
  @Mutation(() => String)
  async logout(
    @Context() context: IContext, //
  ) {
    return this.authService.logout({ req: context.req, res: context.res });
  }

2. auth.service.ts에 Token을 검증 해줄 jwt.verify 로직과 logout API 로직 생성

verify({ accessToken, refreshToken }) {
    try {
      const decodedAccessToken = jwt.verify(
        accessToken,
        process.env.JWT_ACCESS_KEY,
      );
      const decodedRefreshToken = jwt.verify(
        refreshToken,
        process.env.JWT_REFRESH_KEY,
      );
      return { decodedAccessToken, decodedRefreshToken };
    } catch (error) {
      throw new UnauthorizedException('검증되지 않은 토큰입니다.');
    }
  }

  async logout({ req, res }) {
    const accessToken = req.headers.authorization.replace('Bearer ', '');
    const refreshToken = req.headers.cookie.replace('refreshToken=', '');

    const { decodedAccessToken, decodedRefreshToken } = this.verify({
      accessToken,
      refreshToken,
    });

    const date = new Date().getTime();

    const AccessTokenTtl = Math.trunc(
      (decodedAccessToken['exp'] * 1000 - date) / 1000,
    );

    const RefreshTokenTtl = Math.trunc(
      (decodedRefreshToken['exp'] * 1000 - date) / 1000,
    );

    await this.cacheManager.set(`accessToken:${accessToken}`, accessToken, {
      ttl: AccessTokenTtl,
    });

    await this.cacheManager.set(`refreshToken:${refreshToken}`, refreshToken, {
      ttl: RefreshTokenTtl,
    });

    res.setHeader('Set-Cookie', `refreshToken=; path=/; Secure; httpOnly;`);

    return '로그아웃 완료';
  }

3. jwt-access.strategy.ts 와 jwt-refresh.strategy.ts의 validate에 재검증 로직 생성

  async validate(req, payload) {
    const accessToken = req.headers.authorization.replace('Bearer ', '');
    const checkAccessToken = await this.cacheManager.get(
      `accessToken:${accessToken}`,
    );

    if (checkAccessToken)
      throw new UnauthorizedException('로그인이 필요한 서비스입니다.');

    // console.log(payload);
    return {
      email: payload.email,
      id: payload.sub,
    };
  }
profile
백엔드 성장 기록
post-custom-banner

0개의 댓글