카카오 소셜 로그인

// OAuth.js

const env = process.env.NODE_ENV;

const CLIENT_ID = '~~~';
const REDIRECT_URI =
  env === 'development'
    ? 'http://localhost:3000/oauth/callback/kakao'
    : 'http://goohomt.p-e.kr.s3-website.ap-northeast-2.amazonaws.com/oauth/callback/kakao';

const KAKAO_AUTH_URL = `https://kauth.kakao.com/oauth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code`;

export { KAKAO_AUTH_URL, CLIENT_ID, REDIRECT_URI };
import { KAKAO_AUTH_URL } from '../shared/OAuth';
...

<img
  src={kakaoLoginButton}
  onClick={() => {
    window.location.href = KAKAO_AUTH_URL;
  }}
/>
// App.js

...
{/* 카카오 로그인 후 랜딩되는 redirect uri */}
<Route path="/oauth/callback/kakao" exact component={KakaoLanding} />
// KakaoLanding.js

...

const KakaoLanding = (props) => {
  const dispatch = useDispatch();

  // 인가코드
  let code = new URL(window.location.href).searchParams.get('code');

  React.useEffect(async () => {
    await dispatch(userAction.kakaoLoginAPI(code));
  }, []);

  return <></>;
};

export default KakaoLanding;
  • 카카오에서 인가코드 받아와서 카카오 토큰 생성.
  • 서버로 카카오 토큰 전달 후 jwt 받아옴.
  • jwt를 axios header에 디폴트로 설정해두고, cookie에 로그인을 했다는 flag 등록.
  • refresh token도 header에 담은 이유는 서버에서 token 갱신을 해주기로 해서...
const kakaoLoginAPI = (code) => {
  return function (dispatch, getState, { history }) {
    api({
      method: 'GET',
      url: `https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&code=${code}`,
    })
      .then((res) => {
        api
          .post('/auth/kakaoLogin', {
            token: res.data,
          })
          .then((res) => {
            const accessToken = res.data.loginUser.token.accessToken;
            const refreshToken = res.data.loginUser.token.refreshToken;

            dispatch(checkLogin(accessToken));
            cookies.set('homt6_is_login', 'true', { path: '/' });

            // CSRF 공격에 대한 방지책 생각해보기.
            // cookie 보안 관련 방법 더 알아보기.
            // samesite: strict를 하면 왜 쿠키 저장이 안되는지...
            // 만료기한은 어떻게 잡아야할지...
            cookies.set('homt6_access_token', accessToken, { path: '/' });
            cookies.set('homt6_refresh_token', refreshToken, { path: '/' });
            history.push('/');
          })
          .catch((err) => {
            logger('서버로 토큰 전송 실패', err);
            window.alert('로그인에 실패하였습니다.');
            history.replace('/login');
          });
      })
      .catch((err) => {
        logger('소셜로그인 에러', err);
        window.alert('로그인에 실패하였습니다.');
        history.replace('/login');
      });
  };
};

const getUpdatedAccessTokenAPI = () => {
  return function (dispatch, getState, { history }) {
    api
      .get('/tokens')
      .then((res) => {
        // 최초 access token을 받아와서 쓰다보면 만료가 됨.
        // 갱신된 accesstoken을 받아야함
        // 최초 access token으로 payload 내용을 redux에 담아서 쓰다가 새로고침을 하면 다 날아감.
        // 갱신하는 로직이 어떤지 모르겠지만 프론트에서 요청을 할때마다 갱신된 access token을 받을수 있으면 좋겠다.
        const accessToken = res.data.loginUser.token.accessToken;
        const refreshToken = res.data.loginUser.token.refreshToken;

        cookies.set('homt6_is_login', 'true', { path: '/' });
        cookies.set('homt6_access_token', accessToken, { path: '/' });
        cookies.set('homt6_refresh_token', refreshToken, { path: '/' });

        dispatch(checkLogin(cookies.get('homt6_access_token')));
      })
      .catch((err) => {
        logger('갱신된 토큰 반환 실패', err);
      });
  };
};
...

[CHECK_LOGIN]: (state, action) =>
  produce(state, (draft) => {
    if (action.payload.token != 'undefined') {
      const decoded = jwt_decode(action.payload.token);
      draft.is_login = true;
      draft.user = { nickname: decoded.nickname, userImg: decoded.img };
    }
  }),
  1. kakaoLoginAPI 함수는 로그인을 하고 새로고침 등 세션이 끊기지 않을때만 토큰을 계속 갖고 있음 (로컬 변수에).
  2. 새로고침이 되면 새롭게 토큰을 받아와서 써야함. -> 함수에서 최초 로그인 시에 access token과 refresh token을 쿠키에 저장하기로...
  3. 이때 쿠키에 저장된 토큰이 만료가 된 상황에서 새로고침을 하면 리덕스에도 쿠키에도 유저를 식별할수 있는 장치가 하나도 없어짐.
// App.js

useEffect(() => {
  if (cookie.get('homt6_is_login')) {
    dispatch(userAction.getUpdatedAccessTokenAPI());
  }
});
  1. 그래서 App.js에서 is_login 쿠키가 true일때, 새로운 access token을 반환받을 수 있는 api를 useEffect로 넣어두었다.

추가 해결해야 할 문제

  • CSRF 공격에 대한 방지책 생각해보기.
  • cookie 보안 관련 방법 더 알아보기.
  • samesite: strict를 하면 왜 쿠키 저장이 안되는지...
  • 만료기한은 어떻게 잡아야할지...
profile
빠굥

0개의 댓글