h e 1 1 o !
โGoogle Calendar ์ฐ๋ ์ฑ / Google OAuth ๊ตฌํํ๊ธฐ ๋ณธ๋ฌธ
๐ Google ์์ ๋ก๊ทธ์ธ ํ๋ก์ฐ
- ๊ตฌ๊ธ ๋ก๊ทธ์ธ ์์ฒญ
- Google OAuth 2.0 ์ธ์ฆ ์๋ฒ์ ๋ฆฌ๋๋ ์ ๋์ด ๋ก๊ทธ์ธ ์ฐฝ์ด ํ์
- ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ์ ์๋ฃ(์ ๊ทผ scope ๋์)
- ๋ฏธ๋ฆฌ ์ค์ ํ ๋ฆฌ๋ค์ด๋ ํธ ์ฃผ์๋ก code ์ค
- code์ accesstoken, refreshtoken ๊ตํ
- token ์ ์ฅ
- access ํ ํฐ์ ํค๋์ ํฌํจ์์ผ ์์ฒญ
- access ํ ํฐ ๋ง๋ฃ โ ์ค๋ฅ
- refresh ํ ํฐ์ผ๋ก access ํ ํฐ ๊ฐฑ์ ์์ฒญ
- ์๋ก์ด access ํ ํฐ ์๋ต
- ์๋ก์ด access ํ ํฐ ์ ์ฅ
- access ํ ํฐ์ ํค๋์ ํฌํจ์์ผ ์์ฒญ
๋ก๊ทธ์ธ์ ์ํ ๊ตฌ๊ธ ํ๋ก์ ํธ ์์ฑ, ์ธ์ฆ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ ๋ฌธ์, ๋ธ๋ก๊ทธ์ ๋์์๋ค.
์ด ๊ธ์์๋ ๊ตฌ๊ธ cloud ์ธก ์ค์ ์ ์๋ฃํ์์ ๊ฐ์ ํ๊ณ ํด๋ผ์ด์ธํธ ์ธก ์ฝ๋ ๋ก์ง์ ์์๋ณผ ๊ฒ์ด๋ค.
โ Google ๋ก๊ทธ์ธ ๊ตฌํ
๐ Google ๋ก๊ทธ์ธ ๊ตฌํ ๋ฐฉ๋ฒ ์ข ๋ฅ
- OAuth 2.0 ์ธ์ฆ ํ๋ก์ฐ ์ง์ ๊ตฌํ โ๏ธ
- react-google-login ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- googleapis(node.js ๋ชจ๋)
- Firebase Authentication
- Google Identity Platform
- Google Cloud Identity-Aware Proxy
๋๋ ์ธ์ฆ ํ์๋ ๊ตฌ๊ธ ์บ๋ฆฐ๋ api๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์
๋ด๊ฐ ๊ตฌํํ ์ฑ์ ๊ธฐ๋ฅ์ ๋์ฑ ์ ํฉํ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ์ง์ ๊ตฌํํ๋ค.
์ ์ธ๊ฐ์ง๋ฅผ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ ๊ฒ ๊ฐ๋ค.
๐ธ ๊ตฌ๊ธ ๋ก๊ทธ์ธ ์์ฒญ ์ค๋น
google cloud project ์ค์ ์ ํตํ์ฌ ์๋์ ๋ณ์ ๊ฐ์ ๊ฐ์ง๊ณ ์์ด์ผํ๋ค.
scope๋ฅผ ์ ์ธํ ๋ชจ๋ ๊ฐ์ ํ๊ฒฝ๋ณ์์ ์ ์ฅํ์ฌ ์ฌ์ฉ ํ๋ ๊ฒ์ด ์ข๋ค.
- google client ID
- google client secret
- google API key
- google calendar ID (์บ๋ฆฐ๋ ๊ณต๊ฐ ์ค์ ๊ณผ id๊ฐ ํ์ํ๋ค)
- scope
google cloud project ์ค์ ์ ๋ง์น ํ์๋ ํ์ํ api ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ฌ scope๋ฅผ ๊ตฌ์ฑํ๋ค.

๋๋ calendar event list์ ๋ํ์ฌ get, insert๋ฅผ ํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ์๋์ scope๋ฅผ ๊ตฌ์ฑํ์๋ค.
'<https://www.googleapis.com/auth/calendar.readonly>',
'<https://www.googleapis.com/auth/calendar>',
'<https://www.googleapis.com/auth/calendar.events>',
๐ธ ๊ตฌ๊ธ ์์ ๋ก๊ทธ์ธ ์์ฒญ
OpenID Connect | Authentication | Google Developers
const scopes = [
'<https://www.googleapis.com/auth/calendar.readonly>',
'<https://www.googleapis.com/auth/calendar>',
'<https://www.googleapis.com/auth/calendar.events>',
];
const oAuthURL = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${
process.env.REACT_APP_GOOGLE_CLIENT_ID
}&response_type=code&redirect_uri=http://localhost:3000/oauth2callback&
access_type=offline&prompt=consent&scope=${scopes.join(' ')}`;
const googleOauthHandler = () => {
window.location.assign(oAuthURL);
};
export default googleOauthHandler;
- client_id
- response_type: ๊ตฌ๊ธ์ธก ์ธ์ฆ์ ์ฑ๊ณตํ ์, access token๊ณผ ๊ตํํ๊ธฐ ์ํด ๋ฐ์ ์๋ต ํ์ ์ผ๋ก, code๋ก ์ค์
- redirect_uri: ์ฑ๊ณต ์ ๋ฆฌ๋ค์ด๋ ํธ ์ฃผ์. ์ด ์ฃผ์์ params๋ก code ๊ฐ์ด ์จ๋ค. google cloud์์ ๋ฏธ๋ฆฌ ์ค์ ํ ์ฃผ์์ ์ผ์นํด์ผํ๋ค.
- (์ ํ) access_type: refresh ํ ํฐ์ ๋ฐ๊ธฐ ์ํด์๋ offline์ผ๋ก ์ค์ ํ๋ค
์ต์ด ๋ก๊ทธ์ธ ์ ์ด ๊ฐ์ด offline์ผ๋ก ์ค์ ๋์ด ์์ง ์์ผ๋ฉด ๊ตฌ๊ธ์ refresh ํ ํฐ์ ์๋ตํ์ง ์๋๋ค. ๋ง์ฝ ์ด๋ฏธ ๋ก๊ทธ์ธ์ ์ฑ๊ณตํ๊ณ , ์ดํ์ refresh ํ ํฐ์ด ํ์ํ๋ค๋ฉด ์ฟ ํค๋ฅผ ์ง์์ ๋ค์ ๋ก๊ทธ์ธ ์์ฒญํ๋ค.
ํ์ฉ๋๋ ๊ฐ์ offline ๋ฐ online. ํจ๊ณผ๋ ์คํ๋ผ์ธ ์ก์ธ์ค์ ๋ฌธ์ํ๋์ด ์๋ค. ์ก์ธ์ค ํ ํฐ์ด ์์ฒญ๋๋ฉด offline ๊ฐ์ด ์ง์ ๋์ง ์์ผ๋ฉด ํด๋ผ์ด์ธํธ๋ ๊ฐฑ์ ํ ํฐ์ ์์ ํ์ง ์๋๋ค. - (์ ํ) prompt: ๋ก๊ทธ์ธ ํ ๋๋ง๋ค refresh ํ ํฐ์ ๋ฐ์์ค๊ธฐ ์ํด consent๋ก ์ค์ ํ๋ค.
prompt consent๋ ์ฌ์ฉ์์๊ฒ Google ๊ณ์ ์ ์ก์ธ์คํ๋ ค๋ ์๋ํํฐ ์ฑ ๋๋ ์น์ฌ์ดํธ์์ ์์ฒญํ๋ ๊ถํ์ ๋ํ ๋์๋ฅผ ๋ฌป๋ ์ฐฝ์ ๋งํ๋ค. ์ฌ์ฉ์๊ฐ ์ด ์ฐฝ์์ ๊ถํ์ ํ์ฉํ๋ฉด ํด๋น ์ฑ์ด๋ ์น์ฌ์ดํธ๋ ์ฌ์ฉ์์ Google ๊ณ์ ์ ์ก์ธ์คํ์ฌ ํ์ํ ์ ๋ณด๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋๋ค. ๊ด๋ จ ๋ฌธ์
prompt (์ ํ์ฌํญ)
์น์ธ ์๋ฒ์์ ์ฌ์ฉ์์๊ฒ ์ฌ์ธ์ฆ ๋ฐ ๋์๋ฅผ ์์ฒญํ๋์ง ์ฌ๋ถ๋ฅผ ์ง์ ํ๋ ๋ฌธ์์ด ๊ฐ์ ๋ชฉ๋ก์ด๋ฉฐ ๊ณต๋ฐฑ์ผ๋ก ๊ตฌ๋ถ๋ฉ๋๋ค.
๊ฐ๋ฅํ ๊ฐ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
โข none
์น์ธ ์๋ฒ๋ ์ธ์ฆ ๋๋ ์ฌ์ฉ์ ๋์ ํ๋ฉด์ ํ์ํ์ง ์์ต๋๋ค. ์ฌ์ฉ์๊ฐ ์์ง ์ธ์ฆ๋์ง ์์๊ณ ์์ฒญ๋ ๋ฒ์์ ๋ํด ์ฌ์ ๊ตฌ์ฑ๋ ๋์๊ฐ ์์ผ๋ฉด ์ค๋ฅ๋ฅผ ๋ฐํํฉ๋๋ค. none์ ์ฌ์ฉํ์ฌ ๊ธฐ์กด ์ธ์ฆ ๋๋ ๋์๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
โขconsent
์น์ธ ์๋ฒ๋ ํด๋ผ์ด์ธํธ์ ์ ๋ณด๋ฅผ ๋ฐํํ๊ธฐ ์ ์ ์ฌ์ฉ์์๊ฒ ๋์๋ฅผ ์์ฒญํฉ๋๋ค.
โขselect_account
์น์ธ ์๋ฒ์์ ์ฌ์ฉ์์๊ฒ ์ฌ์ฉ์ ๊ณ์ ์ ์ ํํ๋ผ๋ ๋ฉ์์ง๋ฅผ ํ์ํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์น์ธ ์๋ฒ์ ์ฌ๋ฌ ๊ณ์ ์ด ์๋ ์ฌ์ฉ์๊ฐ ํ์ฌ ์ธ์ ์ ์ฌ์ฉํ ์ ์๋ ์ฌ๋ฌ ๊ณ์ ์ค์์ ์ ํํ ์ ์์ต๋๋ค. ๊ฐ์ด ์ง์ ๋์ง ์๊ณ ์ฌ์ฉ์๊ฐ ์ด์ ์ ์ก์ธ์ค๋ฅผ ์น์ธํ ์ ์ด ์์ผ๋ฉด ์ฌ์ฉ์์๊ฒ ๋์ ํ๋ฉด์ด ํ์๋ฉ๋๋ค.
'prompt'๋ฅผ 'consent'๋ก ์ค์ ํ๊ฒ ๋๋ฉด ๋งค ๋ก๊ทธ์ธ ๋ง๋ค ์ฌ์ฉ์์๊ฒ ๋์๋ฅผ ์์ฒญํ๊ธฐ ๋๋ฌธ์
๊ฐ์ ๋ก Refresh Token์ ๋ฐ๋๋ก ์ง์ ํ ์ ์๋ค.
์๋๋ ๋์์ ์ป์ ๋ธ๋ก๊ทธ.
Google์ Refresh Token์ ์ฝ๊ฒ ๋ด์ฃผ์ง ์๋๋ค
๐ url ์ฃผ์๋ ๋น์ฐํ ์ค๋ฐ๊ฟํ๋ฉด ์๋๋ค. ๋๋ ์๋์ผ๋ก ๊ณ์ ์ค๋ฐ๊ฟ์ด ๋์ด์ ์ค๋ฅ๊ฐ ๋ฌ์๋ค.
๐ธ ๋ฆฌ๋ค์ด๋ ํธ ํ์ด์ง
import React, { useEffect } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { isLoginAtom, isLoadingAtom } from '../state/atoms';
function Oauth2callback() {
const navigate = useNavigate();
const setIsLogin = useSetRecoilState(isLoginAtom);
const setIsLoading = useSetRecoilState(isLoadingAtom);
useEffect(() => {
setIsLoading(true);
const fetchData = () => {
const url = new URL(window.location.href);
const { searchParams } = url;
const code = searchParams.get('code');
if (code) {
// code๋ก access ํ ํฐ ์์ฒญ
}
};
fetchData();
}, []);
return <div>๊ตฌ๊ธ ์ฐ๋์ค ... </div>;
}
export default Oauth2callback;
- ๊ตฌ๊ธ ๋ก๊ทธ์ธ(์ฌ์ฉ์ ๋์ ๋ฑ)์ด ์ ์์ ์ผ๋ก ์ด๋ฃจ์ด์ง๋ฉด ๊ตฌ๊ธ์ ๋ฏธ๋ฆฌ ์ฝ์๋ ๋ฆฌ๋ค์ด๋ ํธ ์ฃผ์์ params์ code๋ฅผ ๋ฃ์ด ์๋ต
- ๊ทธ๋ผ, ํด๋น ํ์ด์ง์์๋ ์ป์ code๋ฅผ access ํ ํฐ๊ณผ ๊ตํํ์ฌ ์ ์ฅํ๊ณ
- ์ํ๋ ํ์ด์ง๋ก navigate ํ๋ค
- // code๋ก access ํ ํฐ ์์ฒญ ์ฃผ์ ์ฒ๋ฆฌํ ๋ถ๋ถ์ ์๋์์ ์์ธํ ๋ค๋ฃฌ๋ค.
๐ธ ๋ฆฌ๋ค์ด๋ ํธ ํ์ด์ง access ํ ํฐ ์์ฒญ ์ฝ๋
axios
.post(
'<https://oauth2.googleapis.com/token>',
{
code,
client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID,
client_secret: process.env.REACT_APP_GOOGLE_SECRETE,
redirect_uri: '',
grant_type: 'authorization_code',
},
{
headers: {
'Content-Type': 'application/json',
},
},
)
.then((response) => {
console.log(response);
const accessToken = response.data.access_token;
localStorage.setItem('access_token', accessToken);
const refreshToken = response.data.refresh_token;
localStorage.setItem('refresh_token', refreshToken);
setIsLogin(true);
navigate('/');
})
.then(() => {
setIsLoading(false);
})
.catch((error) => console.log(error));
}
โ๏ธ params ์ ํฌํจ๋ code (๊ตฌ๊ธ ์ธก์์ ๊ฐ๊ณ ์๋ code์ ์ด ๊ฐ์ด ์ผ์นํ๋์ง ํ์ธํ๋ค)
โ๏ธ client secret: ์ต์ด ์์ฒญ ์์๋ ํฌํจ๋์ด ์๋ ๊ฐ๊ณผ ๊ฐ๋ค
โ๏ธ client_secret: google cloud ๊ตฌ์ฑํ ๋ ์ ์ฅํ ๊ฐ
post ์์ฒญ์ ์๋ต์๋ access token๊ณผ refresh token ๊ฐ์ด ํฌํจ๋์ด ์๋ค. localstorage์ ์ ์ฅํ๋ค.
โ Development Detail ๋ ๋ณด๊ธฐ
๐ access token, refresh token
access token์ ์ค์ฌ์ฉํ๋ ํ ํฐ, refresh token์ ๊ฐฑ์ ์ ์ํ ๋น์์ฉ ํ ํฐ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ์ฝ๋ค.
โ๏ธ google ์ก์ธ์ค ํ ํฐ์ ๊ธฐ๋ณธ์ ์ผ๋ก 1์๊ฐ(3,600์ด) ๋์ ์ ํจํ๋ค. ์ ํจ ์๊ฐ์ ๋ฐ๋ก ์ค์ ํ ์ ์์ง๋ง ๋ง๋ฃ ์๊ฐ์ ๊ธธ๊ฒ ์ก์ ๊ฒฝ์ฐ, ์ ์ฝ ์กฐ๊ฑด์ ์ถฉ์กฑ์์ผ์ผ ํ๋ค. ์์ ๋ฌธ์์์ ์์ธํ ๋ณผ ์ ์์ผ๋ ์ฐธ๊ณ ํ๋ค.
โ๏ธ access token์ด ๋ง๋ฃ๋๋ฉด refresh ํ ํฐ์ ๋ณด๋ด, ์๋ก์ด access token ์ผ๋ก ๊ฐฑ์ ํ๋ค. ๊ฐ๋จํ๊ฒ ์๋์ ๊ฐ์ ํ๋ฆ์ด๋ค.
ํ ํฐ ์ ํ | ์ธ์ฆ | Google Cloud
๐ก access token, refresh token flow
- access ํ ํฐ ๋ง๋ฃ โ ์ค๋ฅ(401)
- refresh ํ ํฐ์ผ๋ก access ํ ํฐ ๊ฐฑ์ ์์ฒญ(POST)
- ์๋ก์ด access ํ ํฐ ์๋ต
- ์๋ก์ด access ํ ํฐ ์ ์ฅ
- access ํ ํฐ์ ํค๋์ ํฌํจ์์ผ ์์ฒญ
๐ก refresh token์ ์๋ฌด๊ฐ ์๋๋ค
access token ์ด ์ธ์ฆ์ ๊ธฐ๋ณธ์ด์ง, refresh ํ ํฐ์ ์๋๋ค. access ํ ํฐ ๋ง๋ฃ ์ ๋ก๊ทธ์์ ์ฒ๋ฆฌ ํ๋ฉด ๋๋ค.
ํ์ง๋ง ํ ์๊ฐ ๋ง๋ค ๋ก๊ทธ์์-๋ก๊ทธ์ธ์ ํ๋ ๊ฒ์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ข์ง ์๋ค.
์๋น์ค๋ฅผ ๋ณด๋ค ๋งค๋๋ฝ๊ฒ ์ ๊ณตํ๊ธฐ ์ํด refresh ํ ํฐ๋ ์ฌ์ฉํ๋ ๊ฒ ์ข๋ค.
โ ๋ชจ๋ google api ์์ฒญ์ด access token์ ํ์๋ก ํ๋ ๊ฒ์ ์๋๋ค
์ฌ์ฉํ๊ณ ์ ํ๋ ๊ตฌ๊ธ api๊ฐ โ์ธ์ฆโ์ ํ์ํ๋ค๋ฉด, ์์ ์์ฒญ์ ํตํด ๋ฐ์ access token์ ํค๋์ ํฌํจ์์ผ ์์ฒญํด์ผํ๋ค.
๋๋ calendar api๋ฅผ ์ฌ์ฉํ๋๋ฐ, get์ โ์ธ์ฆโ์ด ํ์ํ์ง ์์ง๋ง list insert(post) ์์ฒญ์ ์ธ์ฆ์ด ํ์ํ๋ค.
๊ทธ๋์ list๋ฅผ ์๋ก post ํ ๋๋ง ์ธ์ฆ ํ ํฐ ๊ฐ์ ๊ฐ์ด ๋๊ฒผ๋ค. ํ์ํ api ๋ฌธ์๋ฅผ ๊ผผ๊ผผํ ํ์ธํ์.
๐ธ axios interceptor์ access token ๊ฐฑ์ ์์ฒญ
access token์ด ์ธ์ ๋ง๋ฃ๋ ์ง, ์ฐ๋ฆฌ๋ ๋ชจ๋ฅธ๋ค.
ํ ํฐ์ด ํ์ํ api์ ์์ฒญ์ ํ๋๋ฐ, ๋ง๋ฃ๋์๋ค๊ณ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ณ ๋์์ผ ํ ํฐ์ด ๋ง๋ฃ๋์ง ์ ์ ์๋ค.
๊ทธ๋ผ, ์ด๋ฏธ ์์ฒญํ request๋ ์ด๋ป๊ฒ ํ ๊น?
์ด ๋ axios interceptor๊ฐ ๋ง์ด ์ฐ์ธ๋ค.
axios interceptor๋ฅผ ์ฌ์ฉํ๋ฉด then ๋๋ catch ์ฒ๋ฆฌ๋๊ธฐ ์ ์ ์์ฒญ๊ณผ ์๋ต์ ๊ฐ๋ก์ฑ,
์ํ๋ ์ฝ๋๋ฅผ ์ํ ํ then ๋๋ catch ๋ก ๋ฑ์ด๋ธ๋ค.
๊ทธ๋ผ, ์๋์ ๊ฐ์ ๋ก์ง์ผ๋ก ๊ตฌํํ๋ฉด ๋๋ค.
axios interceptor์ ํจ๊ป axios instance๋ฅผ ์ฌ์ฉํ๋ค.
โ๏ธ axios interceptor ์๋ฌ โ ์ฌ์์ฒญ flow
- response์ 401 access token ๋ง๋ฃ ์๋ฌ๊ฐ ์จ๋ค
- refresh token์ ์ค์ด 'https://oauth2.googleapis.com/token?'โ ์ POST ์์ฒญ์ ๋ณด๋ธ๋ค
- ์๋ก์ด access token์ด ์จ๋ค
- ์ ์ฅํ๋ค
- ์๋ก์ด access token์ ์ฌ์ฉํ์ฌ original request๋ฅผ ์ฌ์์ฒญํ๋ค </aside>
ํ ํฐ ๊ฐฑ์ ํ ์ฌ์์ฒญ ์ฝ๋
PostEventInstance.interceptors.response.use(
(response: AxiosResponse) => {
return response;
},
async (error: AxiosError) => {
const originalRequest = error.config as InternalAxiosRequestConfig;
if (error.response?.status === 401 && !originalRequest.retry) {
originalRequest.retry = true;
const refreshToken = localStorage.getItem('refresh_token');
return axios
.post(
'<https://oauth2.googleapis.com/token?'>,
{
grant_type: 'refresh_token',
client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID,
refresh_token: refreshToken,
client_secret: process.env.REACT_APP_GOOGLE_SECRETE,
},
{
headers: {
'Content-Type': 'application/json',
},
},
)
.then((response) => {
localStorage.setItem('access_token', response.data.access_token);
PostEventInstance.defaults.headers.Authorization = `Bearer ${response.data.access_token}`;
console.log('ํ ํฐ ๊ฐฑ์ ');
originalRequest.headers.Authorization = `Bearer ${response.data.access_token}`;
console.log('์ํ ํฐ ์ฅ์ฐฉ');
return PostEventInstance(originalRequest);
})
.catch((error) => {
console.log(error);
});
}
return Promise.resolve();
},
);
๊ฐฑ์ ์์ฒญ ์ client_id, refresh_token, client_secret ์ ํฌํจ์์ผ์ผํ๋ค.
๐ ์๋ฌ ํํฐ: refresh ํ ํฐ ๋์ ๊ธฐ
refresh ํ ํฐ์ ๋์ ํ๊ณ ๋ง์ ์ค๋ฅ๋ฅผ ๊ฒช์๋ค. ํฌ๊ฒ ์๋ ์ธ ๊ฐ์ง๋ฅผ ์์๋๋ฉด ์ข๋ค.
โ ๊ตฌ๊ธ์ ์ต์ด ๋ก๊ทธ์ธ ์์ refresh token ์์ฒญ์ ํ ๊ฒฝ์ฐ์๋ง refresh token์ ์ ๊ณตํ๋ค
์ต์ด ๋ก๊ทธ์ธ ์ refresh ํ ํฐ์ ์์ฒญํ๊ธฐ ์ํด์๋ Oauth ์์ฒญ ์ **access_type**์ offline์ผ๋ก ์ค์ ํ๋ค.
์ด ๊ฐ์ด offline์ผ๋ก ์ค์ ๋์ด ์์ง ์์ผ๋ฉด ๊ตฌ๊ธ์ refresh ํ ํฐ์ ์๋ตํ์ง ์๋๋ค.
โ๏ธ ๋ง์ฝ ์ด๋ฏธ ๋ก๊ทธ์ธ์ ์ฑ๊ณตํ๊ณ , ์ดํ์ refresh ํ ํฐ์ด ํ์ํ๋ค๋ฉด ์ฟ ํค๋ฅผ ์ง์์ ๋ค์ ๋ก๊ทธ์ธ ์์ฒญํ๋ค
โ refresh token์ ์ต์ด ๋ก๊ทธ์ธ ์์๋ง ์ ๊ณตํ๋ค
๋ก๊ทธ์์ ํ ๋ค์ ๋ก๊ทธ์ธ์ ํ ๋๋ refresh token์ ์๋ต๋์ง ์๋๋ค.
๋ง์ฝ ๋ก๊ทธ์์ ์ token ๊ฐ์ ๋ชจ๋ ์ญ์ ํ๋ค๋ฉด, ๋ค์ ๋ก๊ทธ์ธ ๋ access token๋ง ์จ๋ค.
๊ทธ๋ผ ํด๋น access token ๋ง๋ฃ ์ ํ ํฐ์ ๊ฐฑ์ ํ ์ ์๋ค. ์ด ์ค๋ฅ๋ฅผ ๊ฒช์ผ๋ฉฐ ์ ๋ง์ค๋ฌ์ ๋ค.
โ ๋งค๋ฒ ๋ก๊ทธ์ธํ ๋๋ง๋ค refresh token์ ๋ฐ๊ณ ์ถ๋ค๋ฉด
๊ตฌ๊ธ Oauth ์์ฒญ ์, prompt๋ฅผ consent๋ก ์ค์ ํ๋ค.
์ด๋ ๊ฒ ์ค์ ํ๋ฉด ๋ก๊ทธ์ธ ํ ๋๋ง๋ค ์ฌ์ฉ์์๊ฒ ์ต์ด ๋ก๊ทธ์ธํ ๋์ฒ๋ผ ๊ตฌ๊ธ ๊ณ์ ์ ์ ํํ๊ณ ์ธ์ฆ scope์ ๋ํ ๋์๋ฅผ ์์ฒญํ๋ค.
์ด ๋ refresh token๋ ์๋ก ๋ฐ๊ธํด์ค๋ค.
prompt
prompt consent๋ ์ฌ์ฉ์์๊ฒ Google ๊ณ์ ์ ์ก์ธ์คํ๋ ค๋ ์๋ํํฐ ์ฑ ๋๋ ์น์ฌ์ดํธ์์ ์์ฒญํ๋ ๊ถํ์ ๋ํ ๋์๋ฅผ ๋ฌป๋ ์ฐฝ์ ๋งํฉ๋๋ค. ์ฌ์ฉ์๊ฐ ์ด ์ฐฝ์์ ๊ถํ์ ํ์ฉํ๋ฉด ํด๋น ์ฑ์ด๋ ์น์ฌ์ดํธ๋ ์ฌ์ฉ์์ Google ๊ณ์ ์ ์ก์ธ์คํ์ฌ ํ์ํ ์ ๋ณด๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค.
prompt ๋ฅผ consent ๋ก ์ค์ ํ๊ฒ ๋๋ฉด ๋งค ๋ก๊ทธ์ธ ๋ง๋ค ์ฌ์ฉ์์๊ฒ ๋์๋ฅผ ์์ฒญํ๊ธฐ ๋๋ฌธ์ ๊ฐ์ ๋ก Refresh Token ์ ๋ฐ๋๋ก ์ง์ ํ ์ ์๋ค.
์ด๋ฌํ ๊ณผ์ ์ ๊ฑฐ์ณ ๋์ ๊ตฌ๊ธ ์ธ์ฆ ์์ฒญ ์ฝ๋๊ฐ ๋ง๋ค์ด์ก๋ค.
const scopes = [
'<https://www.googleapis.com/auth/calendar.readonly>',
'<https://www.googleapis.com/auth/calendar>',
'<https://www.googleapis.com/auth/calendar.events>',
];
const oAuthURL = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${
process.env.REACT_APP_GOOGLE_CLIENT_ID
}&response_type=code&redirect_uri=http://localhost:3000/oauth2callback&
access_type=offline&prompt=consent&scope=${scopes.join(' ')}`;
const googleOauthHandler = () => {
window.location.assign(oAuthURL);
};
export default googleOauthHandler;
์ฐธ๊ณ ๋ก refresh token ์ญ์ ๋ง๋ฃ ๋ ์ ์๋ค.
๐ refresh token ๋์ ํ ๋ฐ์ํ๋ ์ค๋ฅ ํด๊ฒฐ ๊ณผ์ ์์ ์ด ๋ฌธ์์์ ๋์์ ์ป์๋ค.
โ ์ฐธ๊ณ ํ ๋ฌธ์
OpenID Connect | Authentication | Google Developers
https://developers.google.com/identity/openid-connect/openid-connect?hl=ko#re-consent
Google์ Refresh Token์ ์ฝ๊ฒ ๋ด์ฃผ์ง ์๋๋ค