[Finble/React] 구글 소셜 로그인 하기
* 해당 프로젝트는 Typescirpt + React 로 진행하였다
우리팀은 구글을 이용한 소셜로그인을 하기로 진행하였고, 생각한 로직은 이렇다
1. 프론트엔드
- 구글 access token 발급 (라이브러리 이용)
- 발급받은 access token 을 서버에 전송
- 서버에서 새로 발급해준 token 을 이용해 다른 api 연결
2. 백엔드
- 프론트에서 받은 access token 으로 회원정보확인
- 회원정보 확인 후 새로운 token 발급 후 프론트에 전송
- 해당 token 으로 다른 api 연결할때 검증
첫번째 시도 : react-google-login 라이브러리
처음에는, 많이들 사용하는 react-google-login 라이브러리를 이용하였다.
이 라이브러리를 이용하는 방법은 간단하다.
npm install react-google-login
or
yarn add react-google-login
으로 먼저 라이브러리 설치를 해준다.
const OnSuccess = async (response: any) => {
console.log(response);
fetch(`${SERVER}/login/`, {
method: 'POST',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ token: response.accessToken }),
})
.then((response) => response.json())
.then((response) => {
console.log(response);
setToken(response.token.access);
});
};
return(
<GoogleLogin
clientId={clientID}
responseType={'id_token'}
onSuccess={OnSuccess}
onFailure={onFailure}
isSignedIn={false}
buttonText=" Google 아이디로 로그인 " />
)
react-google-login 에 있는 GoogleLogin 을 사용해 응답을 받으면
사용자 프로필, token_id, access_token 등 토큰과 필요한 정보를 받을 수 있다.
access_token 을 받으면 이 토큰을 서버에 보내준다
..이렇게 하여 멀쩡히 잘 돌아가던게..!!
// 1.
{error: 'idpiframe_initialization_failed', details: 'You have created a new client application that use…i/web/guides/gis-migration) for more information.'}
// 2.
{error: 'popup_closed_by_user'}
계속해서 이 두가지 에러가 뜨면서 실행되지않았다.ㅜㅠ
해결 방법을 찾아보니까, 사이트의 쿠키 설정을 바꾸라는 등의 해결 방법이 있었지만 그 어느 방법도 해결이 되지 않았다. .. .
알고보니 구글에서 해당 라이브러리를 더 이상 지원하지않고, 권장하지 않는다고한다.
따라서 최근 구글에서 권장하는 라이브러리로 변경했다.
두번째시도 : @react-oauth/google 라이브러리
이 라이브러리도 사용 방법은 크게 어렵지않다.
라이브러리 내에서 제공하는 GoogleOAuthProvider 와 GoogleLogin 을 사용하면 어렵지 않게 jwt 토큰을 얻을 수 있다.
import { GoogleLogin, GoogleOAuthProvider} from '@react-oauth/google';
// .env 파일에 저장해놓은 clientID
const clientID: string = process.env.REACT_APP_CLIENT_ID as string;
return (
<GoogleOAuthProvider clientId={clientID}>
<GoogleLogin
onSuccess={OnSuccess}
shape="circle"
width="447px"
text="signin_with"
onError={() => {console.log('Login Failed');}} />
</GoogleOAuthProvider>
)
하지만 이 경우는 access_token / token_id 의 이름이 아닌
{clientId: '결과', credential: 'jwt 토큰', select_by: 'btn'}
이런 형태로 응답이 온다.
따라서 credential 을 이용해 서버와 통신하면 된다.
구글 로그인 버튼 커스텀
하지만 GoogleLogin을 사용할 경우, 구글 로그인이 버튼을 원하는대로 커스텀하지 못한다
물론 기본으로 제공되는 버튼 안에 여러 옵션이 있지만, 나는 디자인팀에서 전달해준 버튼 그대로 구현해야했다.
버튼을 커스텀하기 위해 GoogleLogin 은 쓰지못하고, useGoogleLogin 을 사용했다.
import {useGoogleLogin} from '@react-oauth/google';
const googleSocialLogin = useGoogleLogin({
onSuccess: (response) => setCode(response.code), // 1회용 auth code 발급
onError: (err) => console.log(err)
flow: 'auth-code',
});
return(
// custom login button
<GoogleCustomButton onClick={googleSocialLogin}>
<ImgContainer width="38px">
<Img src={google} />
</ImgContainer>
<TypoGraphy text="Google 계정으로 로그인" color="#515151" size="input" />
</GoogleCustomButton>
)
하지만 useGoogleLogin 을 사용하면 jwt 형식의 token 을 응답으로 받을 수 없다.
{code: '일회용 auth code', authuser: '0', hd: '내용', prompt: 'consent'}
응답이 이 형식으로 오는데, 여기서 code 는 google에서 access_token을 발급받기 위한 일회용 auth code 이다.
따라서 이 code 를 서버에 넘겨주어 google access token 을 서버에서 요청을 하고, 인증을 해야하는 상황이 되어버렸다.
이렇게 ..
물론 일찍 이걸 알았다면.. 백엔드에 말했겠지만 이미 백엔드는 프론트에서 access token 을 넘겨주는 로직으로 진행을 한 상황이었다.
따라서 프론트에서 access token 을 발급받지 못하면
(1) 백엔드 로직 수정 (2) 디자인팀에서 디자인해준대로 버튼 커스텀 못함
이 두가지 중에 하나는 꼭 일어나는 상황 ㅠㅠ
그래서 어떻게 모두를 만족시킬수 있을까.. 고민하다가
일회용 auth code 를 이용해 프론트에서 직접 https://oauth2.googleapis.com/token 이 경로의 google api 에 접근하여 access token을 발급받는 방법을 선택하였다.
그림으로 그려보자면 이런 흐름으로 진행이 된다.
프론트에서 code 를 먼저 발급받고, 그걸로 access token 을 발급받은 다음에 서버에 넘겨주는 것이다.
즉, 앞에서 사용했던 react-google-login 이 해준 일을 한단계 더 거쳐 직접한것..이라고도 볼 수 있겠다
access token 을 받기 위해 google api 에 접근할때에는,
client_id / client_secret / code / redirect_uri / grant_type 을 바디에 담아 보내주면 된다.
( 난 json 형식으로 보내주었다)
따라서 최종적으로
import {useGoogleLogin} from '@react-oauth/google';
const client_id: string = process.env.REACT_APP_CLIENT_ID as string;
const client_secret: string = process.env.REACT_APP_CLIENT_SECRET as string;
// 1회용 auth code 발급
const googleSocialLogin = useGoogleLogin({
onSuccess: (response) => setCode(response.code),
onError: (err) => console.log(err)
flow: 'auth-code',
});
// 받은 code 로 google api 에서 access token 요청
useEffect(() => {
const data = JSON.stringify({
code: code,
client_id: client_id,
client_secret: client_secret,
redirect_uri: 'http://localhost:3000',
grant_type: 'authorization_code',
});
fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: data,
})
.then((res) => res.json())
.then((res) => setGoogleToken(res.access_token));
}, [code]);
// 위에서 받은 access token을 서버에 전송
useEffect(() => {
fetch(`${SERVER}/login/`, {
method: 'POST',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ token: googletoken }),
})
.then((response) => response.json())
.then((response) => {
console.log(response);
if (!googletoken.trim()) setToken(response.token.access);
});
}, [googletoken]);
return(
// custom login button
<GoogleCustomButton onClick={googleSocialLogin}>
<ImgContainer width="38px">
<Img src={google} />
</ImgContainer>
<TypoGraphy text="Google 계정으로 로그인" color="#515151" size="input" />
</GoogleCustomButton>
)
이렇게하여 무사히 버튼 커스텀도 하고, access token 도 서버에 보내줄 수 있게 되었다!