-
OAuth2 사용해서 react와 함께 소셜로그인 기능 만들기NODE.JS 2021. 10. 29. 21:58
로그인 창에서 많이 볼 수 있는 소셜로그인 기능을 만들어보자.
(spring-react에 이어 두번째 소셜 로그인!)
여기서는 카카오에서 code부분을 access_token으로 생각해서 굉장히 헤메였다...세상에나 마상에나 이런짓을...😵💫
(결국 code로 다시 access_token 받아와서 잘 해결했음!)
REST API
Node js에서 소셜로그인을 구현해내는 방법에는 sdk를 이용한 방법도 존재하지만 Rest api를 통해서 구현하기로 했다.
(백엔드에서 받아서 받은 토큰으로 사용자 정보를 처리할 수 있게)
리액트에서 모든과정은 authorization_code를 받고 나서 access_token을 받아오도록 하고 싶었으나 frontend에서 토큰을 가져오려 시도하면 네이버는 cors오류가 발생했다. 그래서 부득이하게 네이버는 response_type=token으로 설정하여 진행했다. )
(여기서 프론트엔드에서 해당 URL을 부르면 cors오류가 발생한다는 것은 다음 사이트를 참고하여 알게 되었다)
https://developers.naver.com/forum/posts/29165
개발자 포럼 - NAVER Developers
developers.naver.com
https://forum.worksmobile.com/kr/posts/100025
서비스 API Access Token 발급 문제
안녕하세요. 서비스 API Access Token 발급과 관련해서 문의드립니다. Drive API 이용을 위해서 가이드를 따라 API ID, consumerKey를 발급받아 https://auth.worksmobile.com/ba/{API ID}/service/authorize 위의 링크를 통해
forum.worksmobile.com
프로젝트 계정 만들기
https://dodop-blog.tistory.com/249
프로젝트 생성 과정은 전에 했던 방법과 매우 동일하므로 생략...!
(redirect_uri 는 http://localhost:3000/auth/소셜 코드로 만들어 일단 프론트에서 코드를 받았다)
env.js 생성
export const GOOGLE_CLIENT_ID = "구글 클라이언트 id" export const GOOGLE_CLIENT_SECRET = "구글 클라이언트 secret" export const KAKAO_CLIENT_ID = "카카오 클라이언트 id" export const KAKAO_CLIENT_SECRET = "카카오 클라이언트 secret" export const NAVER_CLIENT_ID = "네이버 클라이언트 id" export const NAVER_CLIENT_SECRET = "네이버 클라이언트 secret" //자신이 설정한 redirect_uri의 공통된 부분 export const OAUTH_REDIRECT_URI = "http://localhost:3000/auth/" //구글에서는 scope를 email, profile로 설정 (token 바로 받아온다) export const GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/auth?client_id=" + GOOGLE_CLIENT_ID + "&redirect_uri=" + OAUTH_REDIRECT_URI + "google" + "&scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile" + "&response_type=token" + "&include_granted_scopes=true" //카카오에서는 response_type을 코드로 하여 보안코드를 먼저 받아왔다. export const KAKAO_AUTH_URL = "https://kauth.kakao.com/oauth/authorize?client_id=" + KAKAO_CLIENT_ID + "&redirect_uri=" + OAUTH_REDIRECT_URI + "kakao" + "&response_type=code" + "&scope=profile_nickname,profile_image,account_email,gender" export const KAKAO_TOKEN_URL = "https://kauth.kakao.com/oauth/token?client_id=" + KAKAO_CLIENT_ID + "&client_secret=" + KAKAO_CLIENT_SECRET + "&redirect_uri=" + OAUTH_REDIRECT_URI + "kakao" + "&grant_type=authorization_code" + "&code=" //네이버에서도 프론트엔드에서 토큰을 받는 것이 불가능했기에 response_type을 토큰으로 해서 먼저 가져왔다. export const NAVER_AUTH_URL = "https://nid.naver.com/oauth2.0/authorize?client_id=" + NAVER_CLIENT_ID + "&redirect_uri=" + OAUTH_REDIRECT_URI + "naver" + "&response_type=token" + "&state=state" export const NAVER_TOKEN_URL = "https://nid.naver.com/oauth2.0/token?client_id=" + NAVER_CLIENT_ID + "&client_secret=" + NAVER_CLIENT_SECRET + "&redirect_uri=" + OAUTH_REDIRECT_URI + "naver" + "&grant_type=authorization_code" + "&code="
authorization code 받아오는 URL과 token을 받아오는 URL을 프론트엔드 env.js파일에 생성해주었다. (gitignore를 설정하여 노출되지 않도록 했다)
페이지 생성해주고 받은 토큰 넘겨주기
<Route exact path="/auth/:social" component={SocialLogin}/>
function SocialLogin(props) { const socialName = props.match.params.social //해당부분은 response_type을 어떤걸로 했느냐에 따라서 달라졌다. var params = socialName === "google" ? props.history.location.hash.substring(1) : socialName === "kakao" ? props.history.location.search.substring(1) : props.history.location.hash.substring(1) const dispatch = useDispatch() useEffect(() => { getAccessToken(params) }, [params]) const getAccessToken = async(params)=>{ if(socialName === "google"){ params = params.split("&") var param = new Array() var key, value for(var i = 0; i<params.length; i++){ key = params[i].split("=")[0] value = params[i].split("=")[1] param[key]=value } dispatch(socialLogin(socialName, param["access_token"])) }else if(socialName === "kakao"){ await axios.post(KAKAO_TOKEN_URL+params.substring(5)) .then(res=>{ dispatch(socialLogin(socialName, res.data.access_token)) }) }else if(socialName === "naver"){ params = params.split("&") var param = new Array() var key, value for(var i = 0; i<params.length; i++){ key = params[i].split("=")[0] value = params[i].split("=")[1] param[key]=value } dispatch(socialLogin(socialName, param["access_token"])) } } return ( <div> </div> ) } export default SocialLogin
google과 naver는 token그 자체를 넘겨주도록 설정했으므로 hash로 받아주고, kakao는 보안코드를 search파트로 넘겨서 받았다.
constant, action, reducer
export const SOCIAL_LOGIN_REQUEST = 'SOCIAL_LOGIN_REQUEST' export const SOCIAL_LOGIN_SUCCESS = 'SOCIAL_LOGIN_SUCCESS' export const SOCIAL_LOGIN_FAIL = 'SOCIAL_LOGIN_FAIL'
export const socialLogin = (socialName, access_token) => async (dispatch)=>{ dispatch({ type:SOCIAL_LOGIN_REQUEST }) try{ const {data} = await axios.post('/api/users/socialLogin', {socialName, access_token}) console.log(data) dispatch({ type:SOCIAL_LOGIN_SUCCESS, payload:data }) localStorage.setItem("userInfo", JSON.stringify(data)) }catch(error){ dispatch({ type:SOCIAL_LOGIN_FAIL, payload: error.response && error.response.data.message ? error.response.data.message : error.message }) } }
case SOCIAL_LOGIN_REQUEST: return {loading:true} case SOCIAL_LOGIN_SUCCESS: return {loading:false, userInfo:action.payload} case SOCIAL_LOGIN_FAIL: return {loading:false, error:action.payload}
해당 action에 대한 변화를 저장해주는 action, reducer를 만들어준다.
Route
userRouter.post('/socialLogin', async(req, res)=>{ try{ const {socialName, access_token} = req.body var fullname, username, email, password, avatar, gender if(socialName === "google"){ const res = await axios.get(`https://www.googleapis.com/oauth2/v1/userinfo?&access_token=${access_token}`) //토큰으로 받아온 정보를 처리 }else if(socialName === "kakao"){ const res = await axios.get("https://kapi.kakao.com/v2/user/me", { headers: { Authorization: `Bearer ${access_token}` } }) //토큰으로 받아온 정보를 처리 }else if(socialName ==="naver"){ const res = await axios.get('https://openapi.naver.com/v1/nid/me',{ headers: { Authorization: `Bearer ${access_token}` } }) //토큰으로 받아온 정보를 처리 } let newUserName = username.toLowerCase().replace(/ /g, '') var user = await User.findOne({email:email}) var token; //이미 존재하는 사용자라면 로그인한 매체가 다를때 오류르 발생시키고, 아니라면 변한 정보를 저장한다. if(user){ if(user.social !== socialName){ return res.status(400).json({message:"You already have an acoount with this email."}) } if(user.avtar!== avatar || user.fullname !== fullname){ user.avatar = avatar user.fullname = fullname user = await user.save() } token = generateToken(user) }else{ //존재하지 않는 사용자라면 정보를 가지고 회원가입 시킨다. const createdUser = new User({fullname, username:newUserName, email, password, social:socialName, gender}) user = await createdUser.save() token = generateToken({id:createdUser._id}) } res.status(200) .send({ token, user:{ ...user._doc, password:'' }, msg:"Social Login Success" }) }catch(err){ return res.status(500).json({message:err.message}) } })
넘겨받은 토큰을 가지고 정보를 가져오도록 한다.
로그인 페이지에 버튼 추가
<div className="auth_social_buttons"> <button className="auth_social_button" style={{backgroundColor:"#3266cf"}}> <a href={GOOGLE_AUTH_URL}> <div style={{color:"white"}}> Continue with GOOGLE </div> <div className="auth_social_button_image"> <img src={google} alt="" /> </div> </a> </button> <button className="auth_social_button" style={{backgroundColor:"#F5DA0C"}}> <a href={KAKAO_AUTH_URL}> <div style={{color:"#2E1413"}}> Continue with KAKAO </div> <div className="auth_social_button_image" style={{border:"2px solid #2E1413"}}> <img src={kakao} alt="" /> </div> </a> </button> <button className="auth_social_button" style={{backgroundColor:"#1AC049"}}> <a href={NAVER_AUTH_URL} style={{color:"white"}}> <div> Continue with NAVER </div> <div className="auth_social_button_image" style={{border:"2px solid white"}}> <img src={naver} alt="" /> </div> </a> </button> </div>
만들어 놓은 해당링크로 버튼을 생성해주면 완료!
로그인 버튼이 잘 생성되었다. ( 참고한 사이트 )
https://dukdukz.tistory.com/entry/카카오-로그인-API-사용
21.05.21 카카오 로그인( API 사용 )
내 서버와 카카오 서버를 연결하는 방법을 알아보겠다 원래는 router로 설정해서 빼줘야 하는데 이번 코드에서는 이해를 위해 server.js에서 app으로 한다. [전체적인 구조] 더보기 카카오 개발
dukdukz.tistory.com
https://velog.io/@nara7875/Node.js-kakao-login-api-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0
[Node.js] kakao login api 가져오기
kakao login api를 가져오는 방법
velog.io
https://devhaks.github.io/2019/05/31/oauth2/
카카오 로그인 연동을 통한 OAuth2 이해하기
OAuth2 를 이해하기 위해 카카오 로그인 연동을 통해 설명합니다.
devhaks.github.io
https://developers.naver.com/docs/login/api/api.md
네이버 로그인 API 명세 - LOGIN
'네이버 로그인 API는 네이버 로그인 인증 요청 API, 접근 토큰 발급/갱신/삭제 요청API로 구성되어 있습니다. 네이버 로그인 인증 요청 API는 여러분의 웹 또는 앱에 네이버 로그인 화면을 띄우는 AP
developers.naver.com
https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
https://eventhorizon.tistory.com/26
node.js 로 google oauth2 테스트 하기 rest api만 이용
google oauth2 node.js 테스트 서버 google library 를 이용하지 않고 순수하게 rest api만을 가지고 구글 연동을 테스트한다. (2017년 5월 테스트 정상적으로 진행됨을 확인하였다.) var request = require('requ..
eventhorizon.tistory.com
https://www.daleseo.com/google-oauth/
OAuth 2.0으로 구글 API 호출하기
Engineering Blog by Dale Seo
www.daleseo.com
'NODE.JS' 카테고리의 다른 글
Social Media 만들기 - 22) Call 기능 만들기 ( Web RTC사용하기 (peer)) (0) 2021.08.08 Social Media 만들기 - 21) message 기능 만들기 (intersection Observer(infiniteScrolling) , online-offline 정보 가져오기) (0) 2021.08.07 (mongoDB) findOneAndUpdate가 update된 항목을 return 하지 않을 때 (0) 2021.08.05 Social Media 만들기 - 20) socket io 사용(notify 생성하고 전달하기) (0) 2021.08.04 Social Media 만들기 - 18) SavePost 구현하기 (0) 2021.08.01