🐽 모아모아는 자체적인 회원가입과 로그인 기능이 없고, 소셜로그인으로만 로그인이 가능하다. 회원가입의 장벽이 있을 것이라 생각해서 사용자 접근성 관점에서 소셜로그인으로 쉽게 모아모아 서비스를 사용하도록 결정했다.
소셜 로그인 구현 방법에는 여러가지가 있지만 모아모아 팀에서는 아래와 같이 구현하였다.
const loginUrl = `${process.env.REACT_APP_LOGIN_BASE_URL}/api/oauth2/authorization/${type}?redirect_uri=${process.env.REACT_APP_LOGIN_REDIRECT_URL}`; window.location.assign(loginUrl);
access token
과 refresh token
을 만들어 프론트 측에 보내기 - redirect
access token
과 refresh token
을 받으면 로그인 성공했다고 판단하여 메인페이지로 redirect
참고
localStorage vs Cookie
서버에서 발급해주는 JWT
를 이용하여 사용자 정보를 조회할 수 있기 때문에 탈취당하지 않도록 클라이언트 측에서 잘 보관해야 한다.
XSS (Cross Site Scripting)
- 공격자가 악의적인 js 코드를 웹 브라우저에서 실행시키는 것
- 이 방법으로 브라우저에 저장된 중요 정보들을 탈취 가능하다.
CSRF (Cross Site Request Forgery)
- 정상적인 request를 가로채서 변조된 request를 보내 악의적인 동작을 수행하는 공격
XSS
에 취약하다. CSRF
공격에는 안전하다.🐽 모아모아에서는 만료 시간이 30분인 access token
을 localStorage
에 저장하고, 상대적으로 만료시간이 긴 refresh token
을 httpOnly
옵션을 가진 cookie
에 저장하기로 결정했다!!
Authorization header
에 Bearer {access token}
형태로 넣는다.reissue
요청을 보낸다. 아니라면 api를 호출한다.Authorization header
에 Bearer {access token}
형태로 넣어 api를 호출한다.export const setToken = async () => {
const token = localStorage.getItem(process.env.REACT_APP_TOKEN_KEY);
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
const expiredAt = new Date(
localStorage.getItem(process.env.REACT_APP_EXPIRED_AT_KEY),
);
const today = new Date();
const diffTime = expiredAt.getTime() - today.getTime();
if (diffTime <= 30000) {
const { data } = await axios.get(`${baseUrl}/reissue`, {
withCredentials: true,
});
today.setMinutes(today.getMinutes() + 30);
localStorage.setItem(process.env.REACT_APP_TOKEN_KEY, data.accessToken);
localStorage.setItem(process.env.REACT_APP_EXPIRED_AT_KEY, today);
axios.defaults.headers.common[
'Authorization'
] = `Bearer ${data.accessToken}`;
}
};
// 모든 api를 호출하기 전에 setToken 함수를 호출하여 실행해야 한다.
소셜로그인을 axios get
요청으로 처리했는데 아래와 같이 CORS
문제가 발생했다.
✨ 해결
소설 로그인 요청은 redirect_uri
로 리다이렉트 되어야 해서 비동기 통신 방식으로 요청하면 안된다. 리다이렉트받을 수 있도록 브라우저 창의 url
를 바꿔야 한다.
참고 : 소셜로그인 CORS 문제
백엔드 측 서버를 배포를 하고 프론트는 로컬에서 배포된 서버를 이용했다. redirect
된 url
에서 access token
은 잘 받았는데 쿠키에 refresh token
이 없는 상황이 계속 발생했다. 그 이유는 프론트와 백엔드 도메인이 일치해야 쿠키 전달이 가능하다.
✨ 해결
어떻게 해결할지 백엔드 분이랑 이야기를 많이 했다.
access token
이 필요하고 access token
만료 시간이 지나면 refresh token
이 필요했다.access token
을 받으면 되지만, refresh token
을 이용하여 access token
을 재발급받는 api 테스트도 로컬에서 필요했다.CORS
관련 설정도 다 해주셨고 백엔드 서버에 변경사항이 생길 때마다 빌드 파일을 보내주셨다. 🙇♀️🙇♂️🙇♀️모아모아 백엔드 서버 로컬로 실행하기
- jar 파일 실행하기 (
java -jar {jar 파일 경로}.jar
)Redis-x64-3.0.504.msi
설치하기
http://localhost:8080
로 api를 호출해야 하고, 배포된 사이트에서는 https://moamoadev.shop
로 api를 호출해야 한다. 개발과 배포 상태에서의 백엔드 서버 url를 구분하기 위해 env
파일을 활용했다.📝 env 파일 활용하기
- 환경변수를 관리할 수 있고,
development
과production
여부에 따라 환경변수 값을 다르게 지정할 수 있다.CRA
에서는 환경변수를 사용하기 위해서dotenv
모듈이 설치되어 있으며, 루트 디렉토리에.env
파일을 만드면 된다.- 환경변수명은 반드시
REACT_APP
으로 시작해야 한다..env
: 기본파일.env.development
: 개발자 환경에서 로딩됨// .env.development REACT_APP_LOGIN_BASE_URL = http://localhost:8080 REACT_APP_LOGIN_REDIRECT_URL = http://localhost:3000/oauth/redirect REACT_APP_BACKEND_BASE_URL = http://localhost:8080 REACT_APP_TOKEN_KEY = TOKEN REACT_APP_EXPIRED_AT_KEY = EXPIRED_AT
.env.production
: 배포 환경에서 로딩됨// .env.production REACT_APP_LOGIN_BASE_URL = https://moamoadev.shop REACT_APP_LOGIN_REDIRECT_URL = https://moamoadev.shop/oauth/redirect REACT_APP_TOKEN_KEY = TOKEN REACT_APP_EXPIRED_AT_KEY = EXPIRED_AT
로그인 기능을 구현하는 것이 백엔드와의 첫 협업 경험이었다. 소셜 로그인 기능을 함께 구현하면서 쿠키 이슈를 해결하는 것이 가장 어려웠다.😭 다른 도메인 간에 쿠키를 전달하는 방법이 있지만, 모아모아 팀은 백엔드 서버 관리하시는 분 덕분에 프론트가 로컬에서 CORS
문제 없이 백엔드 서버를 돌릴 수 있었고, 쿠키 전달 문제도 해결되었다. 백엔드 서버를 어떻게 실행하는지 친절히 알려주셔서 감사했다. 🙇♀️🙇♂️🙇♀️
소셜 로그인 기능을 구현하면서 새롭게 배운 것이 많았다.
그리고 .env
파일로 환경변수를 관리하는 방법을 알았지만, development
와 production
여부에 따라 환경변수 값을 다르게 지정할 수 있는 방법을 새롭게 알게 되었다.
이제는 로그인 기능을 구현할 때 팀원들과 협의해야 하는 부분과 프론트가 어떤 역할을 해야 하는지 감을 잡았다!! 🙌