// 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 };
}
}),
// App.js
useEffect(() => {
if (cookie.get('homt6_is_login')) {
dispatch(userAction.getUpdatedAccessTokenAPI());
}
});
추가 해결해야 할 문제