로그아웃 프로세스 -review

JBoB·2023년 2월 21일
0

🐧 30일차 과제 리뷰

기존에 만들었떤 로그인 API에서 로그인을 하면 토큰이 발급된다. 토큰은 한번 만들어지면 변경할 수 없다.

여기서 문제가 생긴다. 로그아웃을 했지만 아직 만료되지 않은 토큰으로 API를 요청할 때, 토큰을 검증해 봤자

아무런 문제가 없이 정상처리 된다.

이를 해결하기 위해 JWT , Redis 를 활용해 로그아웃 API를 만들어 볼것이다.

Redis 설정 전 라이브러리 설치하기

yarn add redis
yarn add cache-manager@4.1.0
yarn add cache-manager-redis-store@2.0.0
yarn add --dev @types/cache-manager-redis-store

JWT 라이브러리 설치하기

yarn add jsonwebtoken

import * as jwt from 'jsonwebtoken' 써주기!
jwt.verify 함수 사용하기!
=> 토큰 유효성을 확인 할 수 있다.
=> jwt.verify() 에 들어가는 매개변수 3개
token: client 에게서 받은 token
sercetkey: token 생성시 사용했던 sercetkey
3번째 인자로 들어간 익명함수: 유효성 검사 결과를 처리할 콜백함수

====== 예제 ======
const secretKey = ''; // 아까 token 만들때 썼던 secretkey
const router = (req, res) => {
  const token = req.headers['x-access-token'] || req.query.token;
  jwt.verify(token, secretKey, 
    function(err, decoded){
      console.log(err) // 유효하지 않은 토큰
      console.log(decoded) // 유효한 토큰, 유저 정보 Object 반환
    }
}
// auth.resolver.ts
@UseGuards(GqlAuthRefreshGuard)
@Mutation(() => String)
  async logout(
    @Context() context: IContext, //
  ) {
    return this.authService.logout({ req: context.req, res: context.res });
  }

// auth.service.ts

// 따로 분리 
verify({ accessToken, refreshToekn}){
    try{
        const decodeAccess = jwt.verify(accessToken,
            process.env.JWT_ACCESS_KEY)

        const decodeRefresh = jwt.verify(refreshToken,
            process.env.JWT_REFRESH_KEY)
        
        return ({decodeAccess,decodeRefresh})
    }catch(e){
        throw new UnauthorizedException('검증이 안된 인증번호입니다!')
    }
}

async logout({req, res}){

    // 0. 토큰만을 받아 올 수 있게 잘라준다.
    const accessToken = req.headers['accessToken'].replace(
        'Bearer ',
        '',
    )
    const refreshToken = await context.req.headers['cookie'].split(
        'refreshToken=',
      )[1];
      const refresh_Token = await refreshToken.split(' path=')[0];
    

    // 1. 검증된 인증번호 인지 확인 . verify 을 바깥으로 빼준다.
    const ({decodeAccess,decodeRefresh}) = this.verify({
        accessToken, refreshToken
    })
    // 2. 레디스에 넣을때 , TTl을 넣어줘서 만료기간을 지정해주자. 
    await this.cacheManager.set(`accessToken:${accessToken}`, 'accessToken', {
        ttl: jwtAccessKey['exp'] - jwtAccessKey['iat'],
      });

      await this.cacheManager.set(
        `refresh_Token:${refresh_Token}`,
        'refresh_Token',
        {
          ttl: jwtRefreshKey['exp'] - jwtRefreshKey['iat'],
        },
      );

      // res는 어디에 쓰지...
      return '로그아웃 만료'
    // strategy에 다시 레디스에 토큰이 저장되어있는지 확인해준다.
}

// jwt - access strategy.ts

constructor(
    @Inject(CACHE_MANAGER)
    private readonly cacheManager: Cache,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: process.env.JWT_ACCESS_KEY,
      passReqToCallback: true,
    });

async validate(req, payload) {
    const myAccessToken = req.headers.authorization.split('Bearer ')[1];
    const cache = await this.cacheManager.get(`accessToken:${myAccessToken}`);
    console.log(myAccessToken, '🦊🦊🦊🦊 hi');
    if (cache !== null)
      throw new UnauthorizedException('로그인이 필요한 유저입니다.');

    return {
      id: payload.sub,
    };
  }
}
profile
간절하고 치열하게 살자

0개의 댓글