인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

김건희님의 프로필 이미지
김건희

작성한 질문수

배달앱 클론코딩 [with React Native]

토큰 질문있습니다!

해결된 질문

작성

·

176

1

제로초님 저번에 강좌에서 낸 숙제라고 하신 앱을 킬때마다 리프레시 토큰 갱신하는것을 구현해 보았습니다. 그런데 제가 옳은 방법으로 했는지 피드백 부탁드립니다!!

 

질문 1.

로그인을 해서 accesstoken과 refreshtoken을 이미 발급받았고 앱을 껐다가 다시 켰을 때를 가정했을 시

   getTokenAndRefresh 함수가 실행되어 (1)의  axios.post(`${Config.API_URL}/refreshToken`,백앤드 요청이 갑니다.

AppInner.tsx

useEffect(() => {
    const getTokenAndRefresh = async () => {
      try {
        const token = await EncryptedStorage.getItem('refreshToken');
        if (!token) {
          return;
        }
        const response = await axios.post( ////////////(1)
          `${Config.API_URL}/refreshToken`,
          {},
          {
            headers: {
              authorization: `Bearer ${token}`,
            },
          },
        );
   
        dispatch(
          userSlice.actions.setUser({
            name: response.data.data.name,
            email: response.data.data.email,
            accessToken: response.data.data.accessToken,
          }),
        );
 
        await EncryptedStorage.setItem( (3) ////////추가된 부분
          'refreshToken',
          response.data.data.refreshToken,
        );
      } catch (error) {
        console.error(error);
        if ((error as AxiosError).response?.data.code === 'expired') {
          // refreshToken 만료되었을 때
          Alert.alert('알림', '다시 로그인 해주세요.');
        }
      }
    };
    getTokenAndRefresh();
  }, [dispatch]);

app.js의 백앤드에서 리프레시 토큰 유효성 검사를 한 후 (2) refreshtoken(새로 추가한 부분)와 accesstoken을 같이 발급해 줍니다. 그러면 다시 AppInner 프론트 페이지로 가서 accesstoken을 리덕스에 저장하고 refreshtoken은1은(2의) await EncryptedStorage.setItem에 저장해줍니다. 

app.js (백앤드)

app.post("/refreshToken", verifyRefreshToken, (req, res, next) => {
  const refreshToken = jwt.sign( // (2) 추가된 부분
    { sub: "refresh", email: res.locals.email },
    jwtSecret,
    { expiresIn: "24h" }
  );
  const accessToken = jwt.sign(
    { sub: "access", email: res.locals.email },
    jwtSecret,
    { expiresIn: "5m" }
  );

  if (!users[res.locals.email]) {
    return res.status(404).json({ message: "가입되지 않은 회원입니다." });
  }
 
  res.json({
    data: {
      accessToken,
      refreshToken, // 추가된 부분
      email: res.locals.email,
      name: users[res.locals.email].name,
    },
  });
});

이렇게 구현해 보았는데요. 결과는 로그인 하면 기존처럼 accesstoken과 refreshtoken이 발급되고 앱을 껐다가 키면  리프레시 토큰 유효성 검사를 한 뒤 유효하면 accesstoken과 refreshtoken을 다시 발급해주며 갱신시켜 줍니다. 만약 refreshotken이 만료되었다면 로그인 페이지로 이동하게 되구요.  시도를 해보니 잘 작동을 하는것 같은데 혹시 제가 무엇을 빼놓지는 않았는지 옳은방법으로 한것인지 제로초님의 의견이 궁금합니다!.

 

질문2.

강의중 제로초님은 accesstoken은 리덕스에 refreshtoken은 암호 저장소에 저장한다고 하셨는데 그러면

백엔드 db에는 둘 중 어느 토큰도 저장하지 않으시는지 궁금합니다.

 

만약 그렇다면 로그인 할때 백엔드에서


app.post("/login", (req, res, next) => {
 
  const refreshToken = jwt.sign(
    { sub: "refresh", email: req.body.email },
    jwtSecret,
    { expiresIn: "24h" }
  );
  const accessToken = jwt.sign(
    { sub: "access", email: req.body.email },
    jwtSecret,
    { expiresIn: "5m" }
  );
  users[req.body.email].refreshToken = refreshToken; /////// 이 부분

  return res.json({
    data: {
      name: users[req.body.email].name,
      email: req.body.email,
      refreshToken,
      accessToken,
    },
  });
});

users[req.body.email].refreshToken = refreshToken;이 코드가 의미하는바가 무엇인지 궁금합니다. 디비용 더미 데이터 같은데 지웠는데도 잘 실행되더라구요. 없어도 되는 코드인가요?...

답변 1

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

1. 코드는 문제가 없는데 액세스 토큰을 리프레시할 때마다 리프레시토큰도 같이 리프레시되는 게 이상합니다. refreshToken은 별도의 라우터로 리프레시하는 게 맞는 것 같습니다.

2. 원래는 리프레시토큰은 DB에 저장해서 리프레시토큰을 갱신하는 요청이 오거나 할 때 DB에 있는 리프레시 토큰과 비교하거나 합니다. 다만 jwt 토큰을 사용하면 jwt 토큰 자체가 변조가 안 되므로 그 자체로 믿고 쓸 수 있어서 DB 접근을 안 할 수 있습니다.

김건희님의 프로필 이미지
김건희

작성한 질문수

질문하기