JWT(JSON Web Token)๋ JSON ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ๋ชจ๋ฐ์ผ์ด๋ ์น์ ์ฌ์ฉ์ ์ธ์ฆ์ ์ํด ์ฌ์ฉํ๋ฉฐ ์ ๋ณด๋ฅผ ์์ ์ฑ ์๊ฒ ์ํธํํ ํ ํฐ์ ์๋ฏธํ๋ค.
๐ Access Token
์ก์ธ์ค ํ ํฐ์ ์ฃผ๋ก ์ธ์ฆ๋ ์ฌ์ฉ์๊ฐ ๋ณดํธ๋ ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์๋ ๊ถํ์ ๋ถ์ฌํ๋ ๋ฐ ์ฌ์ฉ๋๋ ๋ฌธ์์ด์
๋๋ค. ์ฃผ๋ก ์น ์ ํ๋ฆฌ์ผ์ด์
, ๋ชจ๋ฐ์ผ ์ฑ ๋ฑ์์ ์ฌ์ฉ์ ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ์ ์ฐ์
๋๋ค. ์ฌ๋ฌ ์ธ์ฆ ํ๋กํ ์ฝ์์ ์ฌ์ฉ๋๋ฉฐ, ๋ํ์ ์ผ๋ก OAuth 2.0์์ ์ ์๋์ด ์์ผ๋ฉฐ
๊ธฐ๋ณธ์ ์ผ๋ก Access Token์ ๋ค์๊ณผ ๊ฐ์ ํน์ง์ ๊ฐ์ง๊ณ ์๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก Access Token์ ๋ค์๊ณผ ๊ฐ์ ํน์ง์ ๊ฐ์ง๊ณ ์๋ค.
- ์ ํจ ๊ธฐ๊ฐ(Expiration) : Access Token์ ์ผ์ ๊ธฐ๊ฐ ๋์๋ง ์ ํจํ๋ค. ์ผ๋ฐ์ ์ผ๋ก ์งง์ ์๊ฐ(์: 2์๊ฐ) ๋์๋ง ์ ํจํ๋ฉฐ, ์ด ๊ธฐ๊ฐ์ด ์ง๋๋ฉด ์ฌ๋ฐ๊ธ์ด๋ ์๋ก์ด ์ธ์ฆ์ด ํ์ํ๋ค.
- ๊ถํ ๋ฒ์(๋ฒ์) : Access Token์๋ ์ฌ์ฉ์๊ฐ ์ํํ ์ ์๋ ํน์ ์์ ๋๋ ๋ฆฌ์์ค์ ๋ํ ๊ถํ ๋ฒ์๊ฐ ์ง์ ๋๋ค. Scope๋ ํด๋น ํ ํฐ์ผ๋ก ์ด๋ค ์์ ์ด ๊ฐ๋ฅํ์ง๋ฅผ ๊ฒฐ์ ํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
- ๋ฐ๊ธ์(Issuer) : ํ ํฐ์ ๋ฐ๊ธํ ์ธ์ฆ ์๋ฒ(๋ฐ๊ธ์)์ ์ ๋ณด๊ฐ ํ ํฐ์ ํฌํจ๋ ์ ์๋ค. ํด๋ผ์ด์ธํธ๋ ์ด ์ ๋ณด๋ฅผ ์ฌ์ฉํ์ฌ ํ ํฐ์ ์ ๋ขฐ์ฑ์ ํ์ธํ ์ ์๋ค.
- ์๋ช (Signature) : ๋ณด์์ ๊ฐํํ๊ธฐ ์ํด ํ ํฐ์ ๋ฐ๊ธ์์ ์ํด ์๋ช ๋๋ค. ์๋ช ์ ํตํด ํ ํฐ์ด ์ ํจํ๋ฉฐ ์กฐ์๋์ง ์์์์ ๊ฒ์ฆํ ์ ์๋ค.
- ์ฌ์ฉ์ ์๋ณ ์ ๋ณด : Access Token์๋ ์ฌ์ฉ์๋ฅผ ๊ณ ์ ํ๊ฒ ์๋ณํ ์ ์๋ ์ ๋ณด(์ฌ์ฉ์ ID ๋ฑ)๊ฐ ํฌํจ๋ ์ ์๋ค. ์ด๋ฅผ ํตํด ์๋ฒ๋ ํน์ ์ฌ์ฉ์์ ๋ํ ์์ ์ ์ํํ ์ ์๋ค.
๐ Refresh token
access ํ ํฐ์ด ๋ง๋ฃ๊ฐ ๋์ ๊ฒฝ์ฐ access ํ ํฐ์ ์ฌ๋ฐ๊ธ ๋ฐ์ ์ ์๋๋ก ์๋ฒ์ ์์ฒญํ ์ ์๋ค. fresh token์ access token์ ์ฌ๋ฐ๊ธ๋ฐ์ ์ ์๋ token์ด๋ค. ์ด token์ ์๋ฒ์ ์ ์ฅ๋๊ธฐ ๋๋ฌธ์(stateful) refresh token์ด ํด์ปค์ ์ํด ํ์ทจ๋นํ๋ค๊ณ ํ๋จ๋์์ ๋ ์๋ฒ์์ refresh token์ ์ญ์ ํจ์ผ๋ก์จ ๊ฐ์ ๋ก๊ทธ์์์ ์ํฌ ์ ์๋ค.
์ด๋ฐ ํน์ง์ ์ด์ฉํด์ access token + refresh token์ ์กฐํฉ์ ๊ตฌ์ฑํ๋ฉด access token์ ๊ฒฝ์ ์ ์ธ ์ฅ์ ๊ณผ refresh token์ ๋ณด์์ ์ธ ์ฅ์ ์ ๋ ๋ค ์ฑ๊ธธ ์ ์๋ค. access token์ ๋ณด์ ์ ์ผ๋ก ์ทจ์ฝํ๋ 2์๊ฐ ์ ๋๋ก ์งง๊ฒ ๊ฐ์ ธ๊ฐ๊ณ , refresh token์ ์ฒ๋ฆฌ ๋น์ฉ์ด ๋ง์ด ๋ค๊ธฐ ๋๋ฌธ์ 2์ฃผ ์ ๋๋ก ๊ธธ๊ฒ ๊ฐ์ ธ๊ฐ๋ ๋ฐฉ์์ ์ฃผ๋ก ์ฌ์ฉํ๋ค.
์ด๋ฐ ํน์ง์ ์ด์ฉํด์ access token + refresh token์ ์กฐํฉ์ ๊ตฌ์ฑํ๋ฉด access token์ ๊ฒฝ์ ์ ์ธ ์ฅ์ ๊ณผ refresh token์ ๋ณด์์ ์ธ ์ฅ์ ์ ๋ ๋ค ์ฑ๊ธธ ์ ์๋ค. access token์ ๋ณด์ ์ ์ผ๋ก ์ทจ์ฝํ๋ 2์๊ฐ ์ ๋๋ก ์งง๊ฒ ๊ฐ์ ธ๊ฐ๊ณ , refresh token์ ์ฒ๋ฆฌ ๋น์ฉ์ด ๋ง์ด ๋ค๊ธฐ ๋๋ฌธ์ 2์ฃผ ์ ๋๋ก ๊ธธ๊ฒ ๊ฐ์ ธ๊ฐ๋ ๋ฐฉ์์ ์ฃผ๋ก ์ฌ์ฉํ๋ค.
client.interceptors.request.use( function (config) { const user = localStorage.getItem('user'); if (!user) { config.headers["accessToken"] = null; config.headers["refreshToken"] = null; return config } const { accessToken, refreshToken } = JSON.parse(user) config.headers["accessToken"] = accessToken; config.headers["refreshToken"] = refreshToken; return config } )
reqeust๋ฅผ ๋ณด๋ผ๋ localStorage์ token ์ ๋ณด๊ฐ ์๋ค๋ฉด ํค๋์ ํ ํฐ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ณ ์๋ค๋ฉด null๋ก ์ฒ๋ฆฌ๋ฅผ ํ๋ค.
client.interceptors.response.use( function (response) { return response }, async function (error) { if (error.response && error.response.status === 403) { try { const originalRequest = error.config; const data = await client.get('auth/refreshtoken') if (data) { const {accessToken, refreshToken} = data.data localStorage.removeItem('user') localStorage.setItem('user', JSON.stringify(data.data, ['accessToken', 'refreshToken'])) originalRequest.headers['accessToken'] = accessToken; originalRequest.headers['refreshToken'] = refreshToken; return await client.request(originalRequest); } } catch (error){ localStorage.removeItem('user'); console.log(error); } return Promise.reject(error) } return Promise.reject(error) } )
response๋ฅผ ๋ฐ์์ ๋, error๊ฐ ๋ฐ์ํ๊ณ ํด๋น error์ status๊ฐ 403์ด๋ผ๋ฉด ๊ธฐ์กด์ originalRequest๋ฅผ auth/refreshtoken ์ผ๋ก ์ ๋ฌํด ํ ํฐ์ ์ฌ๋ฐ๊ธ ๋ฐ๋๋ก ํ๋ค.
์ฌ๊ธฐ์ 403 ์ด์ธ์ ์ค๋ฅ๊ฐ ๋ค์ด์จ๋ค๋ฉด ํ ํฐ ์ฌ๋ฐ๊ธ์ ์คํจํ๊ฒ์ผ๋ก ์ฒ๋ฆฌ๋ฅผ ํ๋ค.
์ฌ๋ฐ๊ธ ๋ฐ์ ํ ํฐ์ ๋ค์ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ์ ์ฅ ํ๊ณ ํค๋ ๋ถ๋ถ์ ํ ํฐ ์ ๋ณด๋ฅผ ๋ณ๊ฒฝํ๊ณ ๋ค์ originalRequest๋ฅผ ๋ณด๋ธ๋ค.
์ฌ๊ธฐ์ 403 ์ด์ธ์ ์ค๋ฅ๊ฐ ๋ค์ด์จ๋ค๋ฉด ํ ํฐ ์ฌ๋ฐ๊ธ์ ์คํจํ๊ฒ์ผ๋ก ์ฒ๋ฆฌ๋ฅผ ํ๋ค.
์ฌ๋ฐ๊ธ ๋ฐ์ ํ ํฐ์ ๋ค์ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ์ ์ฅ ํ๊ณ ํค๋ ๋ถ๋ถ์ ํ ํฐ ์ ๋ณด๋ฅผ ๋ณ๊ฒฝํ๊ณ ๋ค์ originalRequest๋ฅผ ๋ณด๋ธ๋ค.
import axios from 'axios'; const client = axios.create({ baseURL: 'http://localhost:4000/' }) client.interceptors.request.use( function (config) { const user = localStorage.getItem('user'); if (!user) { config.headers["accessToken"] = null; config.headers["refreshToken"] = null; return config } const { accessToken, refreshToken } = JSON.parse(user) config.headers["accessToken"] = accessToken; config.headers["refreshToken"] = refreshToken; return config } ) client.interceptors.response.use( function (response) { return response }, async function (error) { if (error.response && error.response.status === 403) { try { const originalRequest = error.config; const data = await client.get('auth/refreshtoken') if (data) { const {accessToken, refreshToken} = data.data localStorage.removeItem('user') localStorage.setItem('user', JSON.stringify(data.data, ['accessToken', 'refreshToken'])) originalRequest.headers['accessToken'] = accessToken; originalRequest.headers['refreshToken'] = refreshToken; return await client.request(originalRequest); } } catch (error){ localStorage.removeItem('user'); console.log(error); } return Promise.reject(error) } return Promise.reject(error) } ) export default client;
๋ณด์
CSRF(Cross Site Request Forgery)
CSRF๋ Cross-Site Request Forgery์ ์ฝ์๋ก, ํ ์ฌ์ดํธ์์ ์ธ์ฆ๋ ์ฌ์ฉ์์ ๊ถํ์ ๊ฐ๋ก์ฑ์ด ๋ค๋ฅธ ์ฌ์ดํธ์์ ๊ณต๊ฒฉ์๊ฐ ์๋ํ ๋์์ ์ํํ๊ฒ ํ๋ ๊ณต๊ฒฉ์ด๋ค. ์ด ๊ณต๊ฒฉ์ ์ฌ์ฉ์๊ฐ ์์ ์ ์์ง์ ๋ฌด๊ดํ๊ฒ ๊ณต๊ฒฉ์๊ฐ ์๋ํ ์์ฒญ์ ์
์์ ์ธ ์น์ฌ์ดํธ๋ฅผ ํตํด ์ ์กํจ์ผ๋ก์จ ์ด๋ฃจ์ด์ง๋ค.
๐ CSRF ์๋ ์๋ฆฌ
- ์ฌ์ฉ์๊ฐ ํน์ ์น์ฌ์ดํธ์ ๋ก๊ทธ์ธํ์ฌ ์ธ์ฆ์ ๋ฐ๋๋ค.
- ์ด ์ฌ์ฉ์๊ฐ ๋ค๋ฅธ ์น์ฌ์ดํธ(๊ณต๊ฒฉ์์ ์ฌ์ดํธ)๋ฅผ ๋ฐฉ๋ฌธํ๋ค.
- ๊ณต๊ฒฉ์์ ์ฌ์ดํธ์์๋ ์ฌ์ฉ์์ ๊ถํ์ผ๋ก ํน์ ๋์(์: ๊ธ์ฐ๊ธฐ, ๊ณ์ ๋ณ๊ฒฝ ๋ฑ)์ ์์ฒญํ๋ HTTP ์์ฒญ์ ์์ฑํ๋ค.
- ์ด๋ฅผ ํธ๋ฆฌ๊ฑฐํ๊ธฐ ์ํด ๊ณต๊ฒฉ์๋ ์ด๋ฏธ ๋ก๊ทธ์ธ๋ ์ฌ์ฉ์์ ๋ธ๋ผ์ฐ์ ์์ ์ ์์ ์ธ ์์ฒญ์ ๋ณด๋ผ ์ ์๋ ๋ฐฉ๋ฒ์ ์ฐพ๋๋ค.
๐ ์์ ์๋๋ฆฌ์ค
- ์ฌ์ฉ์ A๊ฐ ์ํ ์น์ฌ์ดํธ์ ๋ก๊ทธ์ธํ๋ค.
- ์ฌ์ฉ์ A๊ฐ ์ ์์ ์ธ ์ด๋ฉ์ผ ๋งํฌ๋ฅผ ํด๋ฆญํ์ฌ ๊ณต๊ฒฉ์์ ์น์ฌ์ดํธ์ ์ ์ํ๋ค.
- ํด๋น ์น์ฌ์ดํธ์์๋ ์ฌ์ฉ์ A์ ๊ถํ์ผ๋ก ์ํ ์น์ฌ์ดํธ์ ์๋์ผ๋ก ์๊ธ ์ด์ฒด๋ฅผ ์์ฒญํ๋ HTTP ์์ฒญ์ ์์ฑํ๋ค.
- ์ด๋ฅผ ํตํด ์ํ ์น์ฌ์ดํธ๋ ์ฌ์ฉ์ A์ ๊ถํ์ผ๋ก ์ด์ฒด๋ฅผ ์คํํ๊ฒ ๋๋ค.
XSS(Cross Site Scripting)
XSS๋ผ๊ณ ๋ถ๋ฆฌ๋ ์ด์ ๋ CSS๊ฐ ์ด๋ฏธ ์ฝ์๊ฐ ์๊ธฐ ๋๋ฌธ์ด๊ณ code injection attack์ด๋ผ๊ณ ๋ ํ๋ค.
XSS๋ ๋ค์ํ ๊ณต๊ฒฉ ๋ฐฉ๋ฒ์ด ์๋๋ฐ ์ฐ์ ์
๊ณต๊ฒฉ์๊ฐ ์๋ํ๋ ์
์์ ์ธ js ์ฝ๋๋ฅผ ํผํด์์ ์น ๋ธ๋ผ์ฐ์ ์์ ์คํ์ํค๋ ๊ฒ ์ ๋๋ก ์๊ณ ์์ผ๋ฉด ๋๋ค.
์ด ๋ฐฉ๋ฒ์ผ๋ก ํผํด์ ๋ธ๋ผ์ฐ์ ์ ์ ์ฅ๋ ์ค์ ์ ๋ณด๋ค์ ํ์ทจ ๊ฐ๋ฅํ๋ค.
ํด๊ฒฐ 1) localStorage์ ์ ์ฅํ๋ ๋ฐฉ๋ฒ
๐ CSRF ๊ณต๊ฒฉ์๋ ์์ ํ๋ค.
๊ทธ ์ด์ ๋ ์๋์ผ๋ก request์ ๋ด๊ธฐ๋ ์ฟ ํค์๋ ๋ค๋ฅด๊ฒ js ์ฝ๋์ ์ํด ํค๋์ ๋ด๊ธฐ๋ฏ๋ก XSS๋ฅผ ๋ซ์ง ์๋ ์ด์ ๊ณต๊ฒฉ์๊ฐ ์ ์์ ์ธ ์ฌ์ฉ์์ธ ์ฒ request๋ฅผ ๋ณด๋ด๊ธฐ๊ฐ ์ด๋ ต๋ค.
๐ XSS์ ์ทจ์ฝํ๋ค.
๊ณต๊ฒฉ์๊ฐ localStorage์ ์ ๊ทผํ๋ js ์ฝ๋ ํ ์ค๋ง ์ฃผ์
ํ๋ฉด localStorage๋ฅผ ๊ณต๊ฒฉ์๊ฐ ๋ด ์ง์ฒ๋ผ ๋๋๋ค ์ ์๋ค.
ํด๊ฒฐ 2) cookie์ ์ ์ฅํ๋ ๋ฐฉ๋ฒ
๐ XSS ๊ณต๊ฒฉ์ผ๋ก๋ถํฐ localStorage์ ๋นํด ์์ ํ๋ค.
์ฟ ํค์ httpOnly ์ต์
์ ์ฌ์ฉํ๋ฉด Js์์ ์ฟ ํค์ ์ ๊ทผ ์์ฒด๊ฐ ๋ถ๊ฐ๋ฅํ๋ค.
๊ทธ๋์ XSS ๊ณต๊ฒฉ์ผ๋ก ์ฟ ํค ์ ๋ณด๋ฅผ ํ์ทจํ ์ ์๋ค.(httpOnly ์ต์ ์ ์๋ฒ์์ ์ค์ ํ ์ ์์) ํ์ง๋ง XSS ๊ณต๊ฒฉ์ผ๋ก๋ถํฐ ์์ ํ ์์ ํ ๊ฒ์ ์๋๋ค.
๊ทธ๋์ XSS ๊ณต๊ฒฉ์ผ๋ก ์ฟ ํค ์ ๋ณด๋ฅผ ํ์ทจํ ์ ์๋ค.(httpOnly ์ต์ ์ ์๋ฒ์์ ์ค์ ํ ์ ์์) ํ์ง๋ง XSS ๊ณต๊ฒฉ์ผ๋ก๋ถํฐ ์์ ํ ์์ ํ ๊ฒ์ ์๋๋ค.
httpOnly ์ต์
์ผ๋ก ์ฟ ํค์ ๋ด์ฉ์ ๋ณผ ์ ์๋ค ํด๋ js๋ก request๋ฅผ ๋ณด๋ผ ์ ์์ผ๋ฏ๋ก ์๋์ผ๋ก request์ ์ค๋ฆฌ๋ ์ฟ ํค์ ํน์ฑ ์ ์ฌ์ฉ์์ ์ปดํจํฐ์์ ์์ฒญ์ ์์กฐํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ณต๊ฒฉ์๊ฐ ๊ท์ฐฎ์ ๋ฟ์ด์ง XSS๊ฐ ๋ซ๋ฆฐ๋ค๋ฉด httpOnly cookie๋ ์์ ํ์ง ์๋ค.
๊ณต๊ฒฉ์๊ฐ ๊ท์ฐฎ์ ๋ฟ์ด์ง XSS๊ฐ ๋ซ๋ฆฐ๋ค๋ฉด httpOnly cookie๋ ์์ ํ์ง ์๋ค.
๐ CSRF ๊ณต๊ฒฉ์ ์ทจ์ฝํ๋ค.
์๋์ผ๋ก http request์ ๋ด์์ ๋ณด๋ด๊ธฐ ๋๋ฌธ์ ๊ณต๊ฒฉ์๊ฐ request url๋ง ์๋ค๋ฉด ์ฌ์ฉ์๊ฐ ๊ด๋ จ link๋ฅผ ํด๋ฆญํ๋๋ก ์ ๋ํ์ฌ request๋ฅผ ์์กฐํ๊ธฐ ์ฝ๋ค.
ํด๊ฒฐ 3 : refresh token ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ผ๋ก๋ refresh token์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด ์๋ค. CSRF ๊ณต๊ฒฉ์ผ๋ก๋ถํฐ ์์ ํ ํ๊ฒฝ์ ์ ์งํ๊ธฐ ์ํด ์ฟ ํค์ SameSite ์์ฑ์ ์ค์ ํ๊ณ , ๋ฆฌํ๋ ์ ํ ํฐ์ ํ์ฉํ์ฌ ์์ฒญ์ ์ ํจ์ฑ์ ๊ฒ์ฆํ๋ ๋ฐฉ๋ฒ์ด๋ค. ๋ฐฑ์๋ api ๊ฐ๋ฐ์์ ์ํต์ด ๊ฐ๋ฅํ๋ค๋ฉด refresh token์ httpOnly ์ฟ ํค๋ก ์ค์ ํ๊ณ
url์ด ์๋ก๊ณ ์นจ ๋ ๋๋ง๋ค refresh token์ request์ ๋ด์ ์๋ก์ด accessToken์ ๋ฐ๊ธ ๋ฐ๋๋ค.
๋ฐ๊ธ ๋ฐ์ accessToken์ js private variable์ ์ ์ฅํ๋ค.
์ด๋ฐ ๋ฐฉ์์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, refresh token์ด CSRF์ ์ํด ์ฌ์ฉ๋๋ค ํ๋๋ผ๋ ๊ณต๊ฒฉ์๋ accessToken์ ์ ์ ์๋ค.
CSRF๋ ํผํด์์ ์ปดํจํฐ๋ฅผ ์ ์ดํ ์ ์๋ ๊ฒ์ด ์๋๊ธฐ ๋๋ฌธ์ด๋ค. ์์ฒญ์ ์์กฐํ์ฌ ํผํด์๊ฐ ์๋ํ์ง ์์ ์๋ฒ ๋์์ ์ผ์ผํค๋ ๊ณต๊ฒฉ๋ฐฉ๋ฒ์ด๊ธฐ ๋๋ฌธ์ refresh token์ ํตํด ๋ฐ์์จ response(accessToken)๋ ๊ณต๊ฒฉ์๊ฐ ํ์ธํ ์ ์๋ค.
๋ฐ๋ผ์ ์ฟ ํค๋ฅผ ์ฌ์ฉํ์ฌ XSS๋ฅผ ๋ง๊ณ refresh token ๋ฐฉ์์ ์ด์ฉํ์ฌ CSRF๋ฅผ ๋ง์ ์ ์๋ค.
๋ฐ๊ธ ๋ฐ์ accessToken์ js private variable์ ์ ์ฅํ๋ค.
์ด๋ฐ ๋ฐฉ์์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, refresh token์ด CSRF์ ์ํด ์ฌ์ฉ๋๋ค ํ๋๋ผ๋ ๊ณต๊ฒฉ์๋ accessToken์ ์ ์ ์๋ค.
CSRF๋ ํผํด์์ ์ปดํจํฐ๋ฅผ ์ ์ดํ ์ ์๋ ๊ฒ์ด ์๋๊ธฐ ๋๋ฌธ์ด๋ค. ์์ฒญ์ ์์กฐํ์ฌ ํผํด์๊ฐ ์๋ํ์ง ์์ ์๋ฒ ๋์์ ์ผ์ผํค๋ ๊ณต๊ฒฉ๋ฐฉ๋ฒ์ด๊ธฐ ๋๋ฌธ์ refresh token์ ํตํด ๋ฐ์์จ response(accessToken)๋ ๊ณต๊ฒฉ์๊ฐ ํ์ธํ ์ ์๋ค.
๋ฐ๋ผ์ ์ฟ ํค๋ฅผ ์ฌ์ฉํ์ฌ XSS๋ฅผ ๋ง๊ณ refresh token ๋ฐฉ์์ ์ด์ฉํ์ฌ CSRF๋ฅผ ๋ง์ ์ ์๋ค.