refreshToken
์ secure
httpOnly ์ฟ ํค
๋ก, accessToken
์ JSON payload๋ก ์ ๋ฌ๋ฐ๋๋ค.refreshToken
์ ์ด์ฉํด ์๋ก์ด accessToken
์ ๋ฐ์์ ์น ์ดํ๋ฆฌ์ผ์ด์
๋ด ์ง์ญ ๋ณ์์ ์ ์ฅํ๊ณ ์ฌ์ฉํ๋ค.ํด๋ผ์ด์ธํธ์์ ์์ ํ ๋ก๊ทธ์ธ / ์ ์ ์ธ์ฆ (Authentication) ๋ฐฉ์์ ๋ํด ์๊ธฐํ๋ ค๋ฉด ๋ค์ ์ธ ๊ฐ์ง๋ฅผ ๋จผ์ ์ดํดํด์ผ ํ๋ค.
localStorage
, ์ฟ ํค, httpOnly
์ฟ ํค)๋จผ์ ์ ์ ์ธ์ฆ ์ฒ๋ฆฌ ํ๋ก์ธ์ค๋ถํฐ ์ด์ผ๊ธฐํด๋ณด์. ๋ ๊ฐ์ง ๋ฐฉ์์ด ์๋ค.
์ธ์
id
๋ฅผ ์ด์ฉํ๋ ๋ฐฉ์
์ด ๋ฐฉ์์์ ์๋ฒ๋ ํน์ ์ ์ ์ ์ ๋ณด๋ฅผ ๋ด์ ์ธ์
์ ์์ฑํ๋ค. (1) ์ ์ ๊ฐ ๋ก๊ทธ์ธํ ๋ (2) ์๋ฒ๋ ์ธ์
์ ์์ฑํ ํ (3) ๊ทธ ์ธ์
์ id
๋ฅผ ํด๋ผ์ด์ธํธ์ ๋ณด๋ด์ฃผ๊ณ (4) ํด๋ผ์ด์ธํธ๋ ์ด id
๋ฅผ ํด๋ผ์ด์ธํธ์ ์ ์ฅํด๋์๋ค๊ฐ (5) ์ธ์ฆ์ด ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ์๋ฒ์ id
๊ฐ์ ๋ณด๋ด๋ฉด (6) ์๋ฒ๋ ๊ทธ id
๋ฅผ ํตํด ์ธ์
์ ๋ถ๋ฌ์ ์ ํจํ์ง ํ์ธํ๋ ๋ฐฉ์์ผ๋ก ์ธ์ฆํ๋ค.
JWT๋ฅผ ์ด์ฉํ๋ ๋ฐฉ์ (ft. refreshToken
, accessToken
)
๋น์ทํ ๋ฐฉ์์ผ๋ก (1) ์ ์ ๊ฐ ๋ก๊ทธ์ธํ ๋ (2) ์๋ฒ๊ฐ ์ธ์ฆ ์ ๋ณด๋ฅผ ๋ณด๋ด์ฃผ๋๋ฐ, ์ํธํ๋ ์๊ทธ๋์ฒ ์ถ๊ฐ๊ฐ ๊ฐ๋ฅํ ๋ฐ์ดํฐ ํจํค์ง์์ ์ธ์ฆ ์ ๋ณด๋ฅผ ๋ด์ ๋ณด๋ด์ค๋ค. (์ด ํจํค์ง๊ฐ JSON Web Token ์ฆ JWT๋ค.) (3) ๋ด๊ธฐ๋ ์ ๋ณด ์ค accessToken
๊ณผ refreshToken
์ด ์ดํ ์ ์ ์ธ์ฆ์ ์ฌ์ฉ๋๋๋ฐ (4) ์ด ์ ๋ณด๋ฅผ ํด๋ผ์ด์ธํธ์ ์ ์ฅํด๋๋ค.
์ข ๋ ์์ธํ ์ค๋ช
ํ๋ฉด ์ค์ง์ ์ธ ์ธ์ฆ ์ ๋ณด๋ accessToken
์ธ๋ฐ ์ผ์ ์๊ฐ์ด ์ง๋๋ฉด ๋ง๋ฃํ๋ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ ์๋ค. (๋ก๊ทธ์ธ ํ ๋ฉฐ์น ๋ค ๋ก๊ทธ์ธ์ด ๋ง๋ฃ๋ผ์ ๋ค์ ๋ก๊ทธ์ธํด์ผ ํ๋ ๊ฒฝํ์ด ์๋๊ฐ?) refreshToken
์ ์ด์ฉํด ๋ก๊ทธ์ธ์ ์ง์์ ์ผ๋ก ์ ์งํ ์๋ ์๋ค. refreshToken
์ ์๋ฒ์ ๋ณด๋ด๋ฉด ๊ทธ๋๋ง๋ค ์๋ก์ด accessToken
์ ๋ฐ๊ธํด ๋๋ ค์ฃผ๋ ๊ฑฐ๋ค. refreshToken
์ฌ์ฉ์ ์ต์
์ด๋ค.
๊ทธ๋ผ ๋ค์ ๋์์ (5) ์ด accessToken
์ ์ ์ ์๊ฒ๋ง ๋ณด์ฌ์ค ์ ์๋ ์ ๋ณด์ ์ ๊ทผํ ๋ ์๋ฒ์ ๋ณด๋ด๋ฉด (6) ์๋ฒ๋ ๊ทธ ํ ํฐ์ด ์ ํจํ์ง ํ์ธํ๋ ๋ฐฉ์์ผ๋ก ์ธ์ฆํ๋ค.
๋ค์์ผ๋ก ๋ณด์ ๊ณต๊ฒฉ์ ์ข ๋ฅ์ ํน์ง์ ๋ํด ์ดํด๋ณด์. ์น ์ดํ๋ฆฌ์ผ์ด์ ๋ณด์ ์ทจ์ฝ์ ์ค ์ ์ ์ธ์ฆ์์ ๋ณดํธ์ ์ผ๋ก ์ด์ฉ๋๋ ์ทจ์ฝ์ ์ ๋ ๊ฐ์ง๋ค: XSS์ CSRF
XSS ๊ณต๊ฒฉ
๊ณต๊ฒฉ์(ํด์ปค)๊ฐ ํด๋ผ์ด์ธํธ ๋ธ๋ผ์ฐ์ ์ Javascript๋ฅผ ์ฝ์
ํด ์คํํ๋ ๊ณต๊ฒฉ์ด๋ค. ๊ณต๊ฒฉ์๊ฐ <input>
์ ํตํด Javascript๋ฅผ ์๋ฒ๋ก ์ ์กํด ์๋ฒ์์ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ๊ฑฐ๋, url์ Javascript๋ฅผ ์ ์ด ํด๋ผ์ด์ธํธ์์ ์คํฌ๋ฆฝํธ ์คํ์ด ๊ฐ๋ฅํ๋ค๋ฉด ๊ณต๊ฒฉ์๊ฐ ์ฌ์ดํธ์ ์คํฌ๋ฆฝํธ๋ฅผ ์ฝ์
ํด XSS ๊ณต๊ฒฉ์ ํ ์ ์๋ค. ์ด๋ ๊ณต๊ฒฉ์๋ Javascript๋ฅผ ํตํด ์ฌ์ดํธ์ ๊ธ๋ก๋ฒ ๋ณ์ซ๊ฐ์ ๊ฐ์ ธ์ค๊ฑฐ๋ ๊ทธ ๊ฐ์ ์ด์ฉํด ์ฌ์ดํธ์ธ ์ฒ API ์ฝ์ ์์ฒญํ ์๋ ์๋ค. ๋ค์ ๋งํ๋ฉด ๊ณต๊ฒฉ์์ ์ฝ๋๊ฐ ๋ด ์ฌ์ดํธ์ ๋ก์ง์ธ ์ฒ ํ๋ํ ์ ์๋ค๋ ๊ฑฐ๋ค.
CSRF ๊ณต๊ฒฉ
๊ณต๊ฒฉ์๊ฐ ๋ค๋ฅธ ์ฌ์ดํธ์์ ์ฐ๋ฆฌ ์ฌ์ดํธ์ API ์ฝ์ ์์ฒญํด ์คํํ๋ ๊ณต๊ฒฉ์ด๋ค. API ์ฝ์ ์์ฒญํ ์ ์๋ ํด๋ผ์ด์ธํธ ๋๋ฉ์ธ์ด ๋๊ตฌ์ธ์ง ์๋ฒ์์ ํต์ ํ๊ณ ์์ง ์๋ค๋ฉด CSRF๊ฐ ๊ฐ๋ฅํ๋ฐ, ์ด๋ ๊ณต๊ฒฉ์๊ฐ ํด๋ผ์ด์ธํธ์ ์ ์ฅ๋ ์ ์ ์ธ์ฆ์ ๋ณด๋ฅผ ์๋ฒ์ ๋ณด๋ผ ์ ์๋ค๋ฉด, ์ ๋๋ก ๋ก๊ทธ์ธํ ๊ฒ์ฒ๋ผ ์ ์ ์ ์ ๋ณด๋ฅผ ๋ณ๊ฒฝํ๊ฑฐ๋ ์ ์ ๋ง ๊ฐ๋ฅํ ์ก์ ๋ค์ ์ํํ ์ ์๋ค. ์๋ฅผ ๋ค์ด CSRF์ ์ทจ์ฝํ ์ํ ์ฌ์ดํธ๊ฐ ์๋ค๋ฉด ๋ก๊ทธ์ธํ ์ฒ ๊ณ์ข ๋น๋ฐ๋ฒํธ๋ฅผ ๋ฐ๊พธ๊ฑฐ๋ ์ก๊ธ์ ๋ณด๋ผ ์ ์๋ ๊ฒ์ด๋ค.
React์์(= ํด๋ผ์ด์ธํธ) ์ ํ๋ก์ธ์ค๋ค์ ๋ฐ๋ผ ์ธ์
id
๋ accessToken
๊ฐ์ ์ธ์ฆ์ ๋ณด๋ฅผ ์ ์ฅํ ๋ (์ธ์ฆ ๋ฐฉ์ 4๋ฒ ๋จ๊ณ ํด๋น) ์ด์ฉํ๋ ์ ์ฅ์๋ ๋ณดํต localStorage
๋ ์ฟ ํค๋ค. ํ์ด์ง๋ฅผ ๋ฆฌํ๋ ์ ํ๊ฑฐ๋ ์ฐฝ์ ๋ซ๊ณ ๋ค์ ์ ์ํ ๋๋ ๋ก๊ทธ์ธ ์ ๋ณด๊ฐ ์ด์ด์ง๋๋ก ๋ ๋ค ๋ธ๋ผ์ฐ์ ์ ์ ์ฅํ๋ ๋ฐฉ์์ด๋ค. ํ์ง๋ง ๋ ๋ฐฉ์์ XSS์ CSRF ๊ณต๊ฒฉ์ ์ทจ์ฝํ ์ ์๋ค.
localStorage
์ ์ฅ ๋ฐฉ์
๋ธ๋ผ์ฐ์ ์ ์ฅ์์ ์ ์ฅํ๋ ๋ฐฉ์์ด๋ค. Javascript ๋ด ๊ธ๋ก๋ฒ ๋ณ์๋ก ์ฝ๊ธฐ / ์ฐ๊ธฐ ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค.
๐ : localStorage ์์ ์ธ์ id, refreshToken ๋๋ accessToken์ ์ ์ฅํด๋๋ฉด XSS ์ทจ์ฝ์ ์ ํตํด ๊ทธ ์์ ๋ด๊ธด ๊ฐ์ ๋ถ๋ฌ์ค๊ฑฐ๋, ๋ถ๋ฌ์จ ๊ฐ์ ์ด์ฉํด API ์ฝ์ ์์กฐํ ์ ์๋ค.
์ฟ ํค ์ ์ฅ ๋ฐฉ์
๋ธ๋ผ์ฐ์ ์ ์ฟ ํค๋ก ์ ์ฅ๋๋๋ฐ, ํด๋ผ์ด์ธํธ๊ฐ HTTP ์์ฒญ์ ๋ณด๋ผ ๋๋ง๋ค ์๋์ผ๋ก ์ฟ ํค๊ฐ ์๋ฒ์ ์ ์ก๋๋ค. Javascript ๋ด ๊ธ๋ก๋ฒ ๋ณ์๋ก ์ฝ๊ธฐ / ์ฐ๊ธฐ ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค.
๐ : ์ฟ ํค ์ ์ฅ ๋ฐฉ์ ์ญ์ ์์ ์ธ์ id, refreshToken, accessToken์ ์ ์ฅํด๋๋ฉด XSS ์ทจ์ฝ์ ์ด ์์ ๋ ๋ด๊ธด ๊ฐ๋ค์ ๋ถ๋ฌ์ค๊ฑฐ๋, API ์ฝ์ ๋ณด๋ด๋ฉด ์ฟ ํค์ ๋ด๊ธด ๊ฐ๋ค์ด ํจ๊ป ์ ์ก๋์ด ๋ก๊ทธ์ธํ ์ฒ ๊ณต๊ฒฉ์ ์ํํ ์ ์๋ค.
๐ : ์ฟ ํค์ ์ธ์ id๋ accessToken์ ์ ์ฅํด ์ธ์ฆ์ ์ด์ฉํ๋ ๊ตฌ์กฐ์ CSRF ์ทจ์ฝ์ ์ด ์๋ค๋ฉด ์ธ์ฆ ์ ๋ณด๊ฐ ์ฟ ํค์ ๋ด๊ฒจ ์๋ฒ๋ก ๋ณด๋ด์ง๋ค. ๊ณต๊ฒฉ์๋ ์ ์ ๊ถํ์ผ๋ก ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ์ก์ ์ ์ํํ ์ ์๋ค.
๐ : ์ฟ ํค์ refreshToken๋ง ์ ์ฅํ๊ณ ์๋ก์ด accessToken์ ๋ฐ์์ ์ธ์ฆ์ ์ด์ฉํ๋ ๊ตฌ์กฐ์์๋ CSRF ์ทจ์ฝ์ ๊ณต๊ฒฉ์ ๋ฐฉ์ดํ ์ ์๋ค. refreshToken์ผ๋ก accessToken์ ๋ฐ์๋ accessToken์ ์คํฌ๋ฆฝํธ์ ์ฝ์ ํ ์ ์๋ค๋ฉด accessToken์ ์ฌ์ฉํด ์ ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
secure
httpOnly ์ฟ ํค
์ ์ฅ ๋ฐฉ์
๋ธ๋ผ์ฐ์ ์ ์ฟ ํค๋ก ์ ์ฅ๋๋ ๊ฑด ๊ฐ์ง๋ง, Javascript ๋ด์์ ์ ๊ทผ์ด ๋ถ๊ฐ๋ฅํ๋ค. secure
์ ์ ์ฉํ๋ฉด https ์ ์์์๋ง ๋์ํ๋ค.
๐ : httpOnly ์ฟ ํค ๋ฐฉ์์ผ๋ก ์ ์ฅ๋ ์ ๋ณด๋ XSS ์ทจ์ฝ์ ๊ณต๊ฒฉ์ผ๋ก ๋ด๊ธด ๊ฐ์ ๋ถ๋ฌ์ฌ ์ ์๋ค.
๐ : httpOnly ์ฟ ํค ์ญ์ refreshToken๋ง ์ ์ฅํ๊ณ accessToken์ ๋ฐ์์ ์ธ์ฆ์ ์ด์ฉํ๋ ๊ตฌ์กฐ๋ก CSRF ๊ณต๊ฒฉ ๋ฐฉ์ด๊ฐ ๊ฐ๋ฅํ๋ค.
๐ : ์ฟ ํค ์ ์ฅ ๋ฐฉ์๊ณผ ๊ฐ์ ์ด์ ๋ก ์ธ์ id, accessToken์ ์ ์ฅํ๋ฉด ์ ๋๋ค.
๐ : httpOnly ์ฟ ํค์ ๋ด๊ธด ๊ฐ์ ์ ๊ทผํ ์๋ ์์ง๋ง XSS ์ทจ์ฝ์ ์ ๋ ธ๋ ค API ์ฝ์ ์์ฒญํ๋ฉด httpOnly ์ฟ ํค์ ๋ด๊ธด ๊ฐ๋ค๋ ํจ๊ป ๋ณด๋ด์ ธ ์ ์ ์ธ ์ฒ ์ ๋ณด๋ฅผ ๋นผ์ค๊ฑฐ๋ ์ก์ ์ ์ํํ ์ ์๋ค.
์ด๋ค ์ ์ฅ ๋ฐฉ์์ ํํด๋ XSS ์ทจ์ฝ์ ์ด ์๋ค๋ฉด ๋ณด์ ์ด์๊ฐ ์กด์ฌํ๋ค (XSS๋ก API ์ฝ์ ๋ณด๋ด๋ ๋ฐฉ์์ผ๋ก ๋ค ๋ซ๋ฆฐ๋ค). ๊ทธ๋ฌ๋ฏ๋ก ์ ์ ์ ๋ณด ์ ์ฅ ๋ฐฉ์์ ๋ฐ๊พธ๋ ๊ฒ๋ง์ผ๋ก๋ ๋ฐฉ์ดํ ์ ์๊ณ , ํด๋ผ์ด์ธํธ์ ์๋ฒ์์ ์ถ๊ฐ์ ์ผ๋ก XSS ๋ฐฉ์ด ์ฒ๋ฆฌ๊ฐ ํ์๋ค.
์: <input>
์์ ์
๋ ฅ๋ ๊ฐ์ด html / Javascript๋ก ์ธ์๋์ง ์๋๋ก ์๋ฒ์์ escape ์ฒ๋ฆฌ๋ฅผ ํด์ค๋ค. ๋ url์ ํตํด Javascript๋ฅผ ์ํํ ์ ์๋๋ก ๋ผ์ฐํ
์ ๊ผผ๊ผผํ๊ฒ ๊ด๋ฆฌํ๋ค. ๋คํ์ธ ๊ฒ์ React๋ ๊ณต๊ฒฉ์๊ฐ string์ html / Javascript๋ฅผ ๋ด์ JSX์ ์ฝ์
ํ ๊ฒฝ์ฐ ์๋์ผ๋ก escape ์ฒ๋ฆฌํ๋ค. (XSS ๋ฐฉ์ด ์ฒ๋ฆฌ๋ ๋ ๋ค๋ฅธ ์ฃผ์ ๊ธฐ์ ์ฌ๊ธฐ์๋ ์ด ์ ๋์์ ๋ง๋ฌด๋ฆฌํ๋ค.)
๋ณธ๋ก ์ ๋ค์ด๊ฐ๊ธฐ ์ํ ์ค๋น๋ ๋ง์ณค๋ค.
์ข
ํฉํด๋ณด๋ฉด ์ธ์
id
๋ฅผ ๋ธ๋ผ์ฐ์ ์ ์ ์ฅํ๋ ๋ฐฉ์์ ์ด๋ค ๋ฐฉ์์ด๋์ง ๋ณด์ ์ํ์์๊ฐ ์์ผ๋ฏ๋ก JWT ์ด์ฉํ ์ธ์ฆ ๋ฐฉ์์ ์ฌ์ฉํ ๊ฒ์ด๋ค. refreshToken
๋ง์ secure
httpOnly
์ฟ ํค์ ์ ์ฅํด CSRF ๊ณต๊ฒฉ์ ๋ฐฉ์ดํ ๊ฒ์ด๋ค. accessToken
์ ์น ์ดํ๋ฆฌ์ผ์ด์
๋ด ๋ก์ปฌ ๋ณ์์ ์ ์ฅํด ์ฌ์ฉํ๋ฉฐ, API๋ฅผ ์์ฒญํ ๋ Authorization
ํค๋์ ๋ฃ์ด ๋ณด๋ด์ค๋ค. XSS ์ทจ์ฝ์ ์ ์ด์ฉํ API ์์ฒญ ๊ณต๊ฒฉ์ ํด๋ผ์ด์ธํธ์ ์๋ฒ์์ ์ถ๊ฐ์ ์ผ๋ก ๋ฐฉ์ด ํด์ผ ํ๋ค.
refreshToken
์ secure
httpOnly
์ฟ ํค๋ก, accessToken
์ JSON payload๋ก ๋ฐ์์์ ์น ์ดํ๋ฆฌ์ผ์ด์
๋ด ๋ก์ปฌ ๋ณ์๋ก ์ด์ฉsecure
์ฟ ํค ์ ๋ฌ์ ํ๋ ค๋ฉด ํ๋ก ํธ(React)์ ๋ก๊ทธ์ธ API๋ฅผ ์ ๊ณตํ ๋ฐฑ์๋(์๋ฒ API)๋ ๊ฐ์ ๋๋ฉ์ธ์ ๊ณต์ ํด์ผํ๋ค. (์: ํด๋ผ์ด์ธํธ: https://shop.abc.com, ์๋ฒ API: https://api.abc.com)
๋ฐฑ์๋๋ HTTP ์๋ต Set-Cookie
ํค๋์ refreshToken
๊ฐ์ ์ค์ ํ๊ณ accessToken
์ JSON payload์ ๋ด์ ๋ณด๋ด์ค์ผ ํ๋ค.
๋จผ์ , React ์ต์๋จ index.js์์ axios์ withCredentials
๋ฅผ true
๋ก ์ค์ ํด์ค์ผ refreshToken
cookie๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์๋ค.
import React from "react";
import ReactDOM from "react-dom";
import axios from "axios";
import App from "./App";
axios.defaults.baseURL = "https://www.abc.com";
axios.defaults.withCredentials = true;
๋ก๊ทธ์ธ ๋ก์ง์ ๊ฐ๋จํ๋ค.
onLogin = (email, password) => {
const data = {
email,
password,
};
axios.post('/login', data).then(response => {
const { accessToken } = response.data;
// API ์์ฒญํ๋ ์ฝ๋ง๋ค ํค๋์ accessToken ๋ด์ ๋ณด๋ด๋๋ก ์ค์
axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;
// accessToken์ localStorage, cookie ๋ฑ์ ์ ์ฅํ์ง ์๋๋ค!
}).catch(error => {
// ... ์๋ฌ ์ฒ๋ฆฌ
});
}
์ด ์น์ ์ zeneo๋ ์ง๋ฌธ์ ๋ฐ๊ณ ์ถ๊ฐ๋์์ต๋๋ค.
JWT ์ธ์ฆ ๋ฐฉ์์์ ์ค์ง์ ์ผ๋ก ์ธ์ฆ๋์๋๋ฅผ ๊ฒฐ์ ํ๋ ๊ฒ์ accessToken
์ด๋ค. ํ์ง๋ง ์ฐ๋ฆฌ๊ฐ ํํ ๊ตฌ์กฐ์์ ๋ธ๋ผ์ฐ์ ์ ์ ์ฅ๋ ๊ฐ์ ์ฟ ํค์ ์ ์ฅ๋ refreshToken
๋ฟ์ด๊ธฐ์ ๋ก์ปฌ์ ์ ์ฅ๋ accessToken
์ ๋ธ๋ผ์ฐ์ ์ฐฝ์ด ๊บผ์ง๊ฑฐ๋ ํ์ด์ง๊ฐ ๋ฆฌํ๋ ์ ๋๋ ๋ฑ ํ์ด์ง๊ฐ ๋ฆฌ๋ก๋ ๋๋ฉด ์ฌ๋ผ์ง๋ค. ๋ํ, accessToken
์ ์ผ์ ์๊ฐ์ด ์ง๋๋ฉด ๋ง๋ฃ๋๋ค. ๊ทธ๋์ (1) ์ด๋ฉ์ผ, ๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํด์ accessToken
์ ๋ฐ์์ค๋ ๋ณดํต ๋ก๊ทธ์ธ ์ฒ๋ฆฌ๋ฟ๋ง ์๋๋ผ ๋ณด๋ค ์์ฑ๋ ์๋ ์ฌ์ดํธ๋ฅผ ๋ง๋ค๊ธฐ ์ํด (2) accessToken
์ด ๋ง๋ฃ๋์ ๋ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง (์๋ฅผ ๋ค์ด ์ํ ์ฌ์ดํธ๊ฐ์ด ๋ณด์์ด ์ค์ํ ์๋น์ค๋ผ๋ฉด ์์ ๋ค์ ๋ก๊ทธ์ธํ๋๋ก ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋์ํฌ ์๋ ์๊ณ , ์๋๋ฉด ์ ์ ๋ชจ๋ฅด๊ฒ ์๋ฒ์์ ์๋ก์ด accessToken
์ ๋ฐ์์์ ๋ก๊ทธ์ธ์ด ์ฐ์ฅ๋๋๋ก ํ ์๋ ์์), (3) ํ์ด์ง ๋ฆฌ๋ก๋ ๋ ๋ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง (2๋ฒ๊ณผ ๊ฐ์ด ์ฌ๋ฌ ๊ฐ์ง ์ฒ๋ฆฌ ๋ฐฉ์์ด ์์)๋ ๊ฒฐ์ ํด์ผ ํ๋ค.
์ด ์์ ์์๋ ์ ์ ๊ฐ ๋ค์ ์ง์ ๋ก๊ทธ์ธํ๋๋ก ์ ๋ํ์ง ์๊ณ ์กฐ์ฉํ ์๋์ผ๋ก ๋ก๊ทธ์ธ ์ฐ์ฅํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํด๋ณด๊ฒ ๋ค. (์์ด๋ก silent refresh๋ผ๊ณ ๋ถ๋ฅธ๋ค.)
accessToken
์ด ๋ง๋ฃ๋์ ๋ ๋ก๊ทธ์ธ ์ฐ์ฅ ์ฒ๋ฆฌ์์์ ์ด์ผ๊ธฐํ 3๊ฐ์ง ์ผ์ด์ค์ ๋์ํ๊ธฐ ์ํด API๊ฐ 2๊ฐ ํ์ํ๋ค.
POST /login
: ์ด๋ฉ์ผ, ๋น๋ฐ๋ฒํธ๋ฅผ ๋ณด๋ด๋ฉด refreshToken
๊ณผ accessToken
์ ๋ฆฌํดํ๋ค.POST /silent-refresh
: ์ฟ ํค์ ๋ด๊ธด refreshToken
์ด ์๋์ผ๋ก ๋ณด๋ด์ง๋ฉด ์๋ก์ด refreshToken
๊ณผ accessToken
์ ๋ฆฌํดํ๋ค.๋ API ๋ชจ๋ HTTP ์๋ต Set-Cookie
ํค๋์ refreshToken
๊ฐ์ ์ค์ ํ๊ณ accessToken
์ JSON payload์ ๋ด์ ๋ณด๋ด์ค์ผ ํ๋ค.
const JWT_EXPIRY_TIME = 24 * 3600 * 1000; // ๋ง๋ฃ ์๊ฐ (24์๊ฐ ๋ฐ๋ฆฌ ์ด๋ก ํํ)
onLogin = (email, password) => {
const data = {
email,
password,
};
axios.post('/login', data)
.then(onLoginSuccess)
.catch(error => {
// ... ์๋ฌ ์ฒ๋ฆฌ
});
}
onSilentRefresh = () => {
axios.post('/silent-refresh', data)
.then(onLoginSuccess)
.catch(error => {
// ... ๋ก๊ทธ์ธ ์คํจ ์ฒ๋ฆฌ
});
}
onLoginSuccess = response => {
const { accessToken } = response.data;
// accessToken ์ค์
axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;
// accessToken ๋ง๋ฃํ๊ธฐ 1๋ถ ์ ์ ๋ก๊ทธ์ธ ์ฐ์ฅ
setTimeout(onSilentRefresh, JWT_EXPIRRY_TIME - 60000);
}
// ์ดํ๋ฆฌ์ผ์ด์
์ด ์คํ๋ ๋๋ง๋ค ๋ค์ ๋ก๊ทธ์ธ ์๋
class App extends Componet {
componentDidMount() {
onSilentRefresh();
}
// ...
}
localStorage
๊ฐ ๋ธ๋ผ์ฐ์ ์ด๋์ ์ ์ฅ๋๋ ๊ฑด์ง, ์ธ์
๊ณผ JWT์ ์ฐจ์ด์ ์ด ๋ญ์ง ์ ๋๊ฐ ๊ถ๊ธํด์ ๊ฐ๋ณ๊ฒ ๊ตฌ๊ธ๋ง ํ๋๋ฐ, ์ฝ๋ค ๋ณด๋ ๋ณด์ ์ทจ์ฝ์ ์ ๋ํด์๋ ์๊ฒ ๋๊ณ , ์ธ์ฆ ๋ฐฉ์์ ๋ฐ๋ผ ๋ธ๋ผ์ฐ์ ์ ์ฅ ๋ฐฉ์์ ๋ฐ๋ผ ๋ฐ์ํ๋ ๋ณด์ ์ด์๋ ๋ค์ํ๋ค๋ ๊ฑธ ์๊ฒ ๋์๋ค. ์ฝ๋ ์๊ฐ์๋ ์ดํดํ ๊ฒ ๊ฐ์์ง๋ง ๊ณต๊ฒฉ ์ด์๋ง๋ค ์ฌ๋ฌ ๋ฐฉ์์ผ๋ก ์ ๊ฐ๋๊ณ , ๋์ ๋ฐฉ์์ ์ ํ๋ ค๋ฉด ์ ์ฒด ๊ทธ๋ฆผ์ ์ดํดํด์ผ ํด์ ๋ธ๋ก๊น
ํ๋ฉฐ ์ ๋ฆฌํด๋ณด์๋ค.
๊ฒฐ๋ก ์ ์ผ๋ก ๊ฐ์ฅ ์์ ํ๋ค๊ณ ์๊ฐํ๋ ์ ์ ์ธ์ฆ ๋ฐฉ์์ ์ ์ํ๊ณ ๋ง๋ฌด๋ฆฌ ์ง์์ง๋ง, ๋ณด์์ด๋ผ๋ ์ปค๋ค๋ ๋น์ฐ์ ์ผ๋ถ๋ฅผ ๋ฐ๊ฒฌํ ์ ๋์ด๋ฆฌ๋ผ. ๋ ๋์ ์ธ์ฆ ๋ฐฉ์์ ์๊ณ ์๊ฑฐ๋ ๋ค๋ฅธ ์๊ฒฌ์ด ์๋ค๋ฉด ๊ณต์ ํด ์ฃผ์๊ธธ. (Please!) ๋ ๊ธ์ ๊ด๋ จ๋ ์ด๋ค ํผ๋๋ฐฑ์ด๋ผ๋ ํ์์ด๋ค. ์์ ๋กญ๊ฒ ์๊ธฐํด ์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ๋ค.
๋ค์ ๊ธ: "https๋ก React ๋ก์ปฌ ํ ์คํ ํ๊ธฐ"
ํด๋ผ์ด์ธํธ ๋ณด์๐๋ ์ ๋ง ์ค์ํ๋ฐ... ์๋์์ด ๊ณต์ ํด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค ๐
์ ์ฉํ ์ ๋ณด ๊ฐ์ฌํฉ๋๋ค! ์ ๋ ๊ฐ๋ฐํ๋ ํ๋ก์ ํธ์ ์ ์ฐธ๊ณ ํด๋ณด๋ ค๊ณ ํด์!!
์๋ ค์ฃผ์ ๋ฐฉ๋ฒ๋๋ก ๊ตฌํํด ๋ณด๊ณ ์๋ ์ด๋ณด ๊ฐ๋ฐ์ ์
๋๋ค.
๋ช๊ฐ์ง ๊ถ๊ธํด์ ์ฌ์ญ๊ณ ์ถ์ต๋๋ค.
axios.defaults.headers.common['Authorization']์ ๊ฐ์ด ์๋ก๊ณ ์นจ์ ํ๊ฑฐ๋ ๋ค๋ฅธ ํ์ด์ง๋ก ์ฎ๊ฒจ๊ฐ๋ ์ ์ง๊ฐ ์๋๋๋ฐ, ์ด๋ป๊ฒ ํด๊ฒฐํ์๋์? ์์ฑํ์ ํฌ์คํธ์์๋ ๋ณด์์ ์ด์ ๋ก ๋ฆฌ๋์ค๋ ๋ก์ปฌ์คํ ๋ฆฌ์ง๋ฅผ ์์จ์ผํ๋ค๊ณ ํ์๋ ๊ฒ์ผ๋ก ์ดํดํ๋๋ฐ์. (์ผ๋จ์ ๋งค๋ฒ accessํ ํฐ์ ์๋ก ๋ฐ๊ธํด์ค์ผ ํ๋ค๊ณ ์ดํด๋ ํ์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด csrf ํ ํฐ์ฒ๋ผ ์ฌ์ฉ๋๋๊น csrf ํ ํฐ์ ๋ฐ๋ก ์ํด๋ ๋ ๊น์?)
๋ฐฑ์๋๋ ์ด๋ค์ ์ฐจ๋ก ์๋ํ๋๋ก ๊ตฌ์ฑํด์ผ ํ ๊น์? ์์ฑํ์ ํฌ์คํธ์์๋ json์ผ๋ก access ํ ํฐ์ ๋ณด๋ด์ฃผ์ด์ผ ํ๋ค๊ณ ํ์ จ์ผ๋ ํ์ด์ง๊ฐ ์ด๋๋๊ฑฐ๋ ์๋ก๊ณ ์นจ ๋ ๋๋ง๋ค ๋ก๊ทธ์ธ ์ฌ๋ถ๋ฅผ ์ฒดํฌํ๋ api๋ฅผ ๋ง๋ค์ด์ refresh ํ ํฐ์ ์ฟ ํค์์ ์ฝ์ด์์ ๊ทธ์๋ฐ๋ผ ๊ฐ์ access ํ ํฐ์ ์๋ก ๋ฐ๊ธํด์ฃผ๋๋ก ํด์ผ ํ ๊น์?
access ํ ํฐ๊ณผ refresh ํ ํฐ ์ฌ์ฉ์๊ฐ ๋ง์ด ์์ด์ ํผ์ ๋ ํํด์ ๋ง๋๋๋ผ ๋ถ์กฑํ ๋ถ๋ถ์ด ์์๋๋ฐ ์ข์ ๋ด์ฉ ๊ณต์ ๊ฐ์ฌํฉ๋๋ค.
์ค ์ฝ๊ธฐ ์ข๊ฒ ์ ๋ฆฌํด์ฃผ์ จ๋ค์ ใ ใ ์ข์ ๊ธ ๊ฐ์ฌํฉ๋๋ค~!
@yaytomato ๋ ์ถ๊ฐ์ ์ธ ์๊ฒฌ์ ๋๋ฆฌ๊ณ ์ ๋๊ธ๋ฌ์๋ด ๋๋ค! ์ด๊ธ์ ์ฝ๋ ๋ค๋ฅธ ๊ฐ๋ฐ์ ๋ถ๋ค๋ ํ๋ฒ ๊ณ ๋ฏผํด ๋ณด์๋ฉด ์ข์๊ฑฐ ๊ฐ์ต๋๋ค ~ ๊ทธ๋ฆฌ๊ณ ๋ฒจ๋ก๊ทธ ํธ๋ ๋ฉ์ ๋์ค์ ๊ฑฐ ์ถํ๋๋ฆฝ๋๋ค !!
ํ ํฐ ๋ฐฉ์์ ์ ๋ง ์์ ํ๊ฐ? Refresh token ์ด ํ์ทจ๋ฅผ ๋นํ๋ฉด ์ด๋ป๊ฒ๋์ง ?
๋ณธ๋ฌธ์ ๋ด์ฉ์ ๋ค์ ์์ฝํ๋ฉด
์ต์ด ๋ก๊ทธ์ธ์์ access token/ refresh token์ ์ฃผ๊ณ ํ์์ access token ์ผ๋ก API๋ฅผ ์ด์ฉํ๋ค๊ฐ ๋ง๋ฃ๋๋ฉด refresh token ์ผ๋ก ๋ค์ access token์ ๋ฐ๊ธ ๋ฐ๋ ๊ตฌ์กฐ์
๋๋ค. ์ด๋ refresh token ์ ์ฟ ํค์ ์ ์ฅํ๋ค๊ณ ํ์
จ์ต๋๋ค.
์ด ์ํฉ์์ ์ ์ ๋์ด์ผ ํ๋ ๊ฒ์ด refresh๋ฅผ ์์ ํ๊ฒ ํด๋ผ์ด์ธํธ๊ฐ ๋ณด๊ดํ๋ ๊ฒ์ธ๋ฐ ์น๋ธ๋ผ์ฐ์ ์์๋ ์ฌ์ค์ ์ด ํ ํฐ์ ์์ ํ๊ฒ ๋ณด๊ดํ ๊ณณ์ ์์ต๋๋ค. ์ค์ ๋ก ์ ๋ ์์ง ์น์์๋ JWT๋ฅผ ์ ๊ทน์ ์ผ๋ก ์ฌ์ฉํ์ง ์๊ณ ์๋ค๊ณ ์๊ฐํฉ๋๋ค. <-- ์ ๊ฐ์ธ์ ์ธ ์๊ฐ (Refresh token์ ํด๋ผ์ด์ธํธ์ ๋ก์ปฌ์์ ๊ฐ๋ ฅํ๊ฒ ์งํค๋ ๋ฐฉ๋ฒ์ด ์๋ค๋ฉด ์ถ๊ฐ์ ์ผ๋ก ๋๊ธ ํ์ํฉ๋๋ค ~)
๊ทธ๋ผ ๋ญ์ผ... ํ ํฐ์ธ์ฆ์ ๊ฒฐ๊ตญ ๋ณด์์ด ์๋๋๊ฑด๊ฐ? https ๋ก ํ๋ฉด ๋ค ๋๋๋๊ฑฐ ์๋๊ฐ? ์ด๋ฐ ์๊ฐ์ ํ์ค ์ ์๋๋ฐ
์๋๋๋ค. ์ ๋ ํ์ฐธ ๋ถ์กฑํ์ง๋ง ์ ๊ฐ ์๊ฐํ๋ ํด๋ผ์ด์ธํธ๊ฐ ์ฑ๊ฒจ์ผํ ๋ณด์์ ์นจํด ์๊ฐ์ ์งง๊ฒ ํ๋๊ฒ ํ๋๊ฒ ์
๋๋ค.
๊ทธ๋์ ํผํด๋ฅผ ์ค์ด๋ ๊ฒ์ด์ง์~
"๋ณด์์ ์์ด์์ ์๋ค"
๋ ๋ง์ ๊ฐ๋ฐ์๋ค์ด ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ์งํค๊ณ ๋ณด์์ ๊ด์ฌ์ ๊ฐ์ง๊ณ ์์ ์ฅ์น๋ฅผ ํ๋ ๊ฒ์ ์ ๋ง์ ๋ง ์ค์ํ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
๊ฐ์ธ์ ์ผ๋ก๋ xss์ ์ทจ์ฝํ๋ฐฉ์์ ์ ๋ถ ์ ๊ฑฐํ๊ณ cookie๋ก ์ ๋ถ ์ ์ฅํ๋ค์
csrf token์ jwtํ ํฐ์ ํฌํจ์ํค๋๊ฑธ ์ ํธํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ํ๋ก ํธ์์๋ jwtํ ํฐ์ ํฌํจ๋ csrf ํ ํฐ์ ํค๋์ ๋ฃ์ด์ ๋ณด๋ด๊ณ
์๋ฒ์์๋ jwt ํ ํฐ ๋ด csrf์ ํค๋๋ก ๋ค์ด์จ csrf์ ๊ฐ๋น๊ต๋ฅผ ํตํด csrf๋ฐฉ์ด๋ฅผ ํฉ๋๋ค.
์ง๊ธ ์๋ ค์ฃผ์๋ ๋ฐฉ๋ฒ์ผ๋ก๋ ๊ฒฐ๊ตญ xss์ ์ทจ์ฝ์ ์ด ๋จ์ต๋๋ค.
์ ๋ ๋ฆฌํ๋ ์ฌํ ํฐ๊ณผ ์์ธ์ค ํ ํฐ์ ๋ชจ๋ ๋ก์ปฌ์คํ ๋ฆฌ์ง์ ๋ฃ๊ณ , ajax call์ ์์ธ์คํ ํฐ์ ํค๋์ ์ ๋ฌํ๋ ์์ผ๋ก ํ๊ณ expired๋๋ฉด ์๋์ผ๋ก ์ฐ์ฅํด์ฃผ๋ ๋ฐฉ์์ ์ฌ์ฉํ๊ณ ์๋๋ฐ ์ด ๊ฒฝ์ฐ์๋ ๊ด์ฐฎ์ง ์์๊น์?
์ ๋ง ์ ๊ฐ์ ๋ด๋นํํ ๋๋ฌด๋๋ฌด ์ข์ ์ ๋ณด๊ธ์ด๋ค์,.... ๊ฐ์ฌํฉ๋๋ค ๊ณต๋ถํ๊ณ ์ถ์ด์ง๋ค์ฅ
ํ์ฌ ํ๋ก์ ํธ ์งํ ์ค์ ๋ก๊ทธ์ธ ๋ณด์ ๊ด๋ จ ํด๊ฒฐ๋ฒ์ ์ฐพ๊ณ ์์๋๋ฐ ๋๋ฌด๋ ์ข์ ํฌ์คํธ ๊ฐ์ฌํฉ๋๋ค !!
๋ก๊ทธ์ธ ๊ตฌํํ๋๋ฐ ๊ฑฐ์ง 1์ฃผ์ผ์ jwt, ์ํ์ด ์ฝ๋ฉ, ๋ชจ์ํดํน ์ด๋ฐ๊ฑฐ ์ฐพ๋ค ์ด ๊ธ์ ๋ฐ๊ฒฌํ๋ค์. ์ฐพ๋ค๋ณด๋ฉด ํญ์ ๋ฆฌํ๋ ์ ํ ํฐ์ ํด๋ผ์ด์ธํธ์ ์ด๋ป๊ฒ ๋ณด๊ดํด์ผํ๋ ์ด์์ ๋ํ ๋ต์ ๋ชป ์ฐพ์์ ์ฌ๊ธฐ์ ๊ธฐ ๋ค๋
์ต๋๋ค. https only๋ secure๋ ๋ฃ์๊บผ๋ฉด ์์ธ์ค ํ ํฐ์ ์ ๊ธฐ ๋ค ๋ฃ์ง.
๊ตณ์ด ๋ฆฌํ๋ ์๋ฅผ ์จ์ผํ๋ ์๋ฌธ๋ ์๊ธฐ๊ณ .(์ ๋ ๊ฒ ๊น์ง ํด๋ ์ฟ ํค ํธ ์ ์๋ ๋ฅ๋ ฅ์๋ฉด ๋ฆฌํ๋ ์๋ ๊ทธ๋ฅ ํธ๋ฆฌ๋๊ฑฐ๋๊น์.)
์ด์งํผ ํธ๋ฆด ์ ์๋๊ฑฐ ์๋ฒ์์๋ ์ด๋ป๊ฒ ํด์ค์ผ ํ๋ ๊ณ ๋ฏผ๊ณ ๋ฏผ ํ์์ต๋๋ค.
๋ณด์๊ณผ ํธ์์ฑ&์ฑ๋ฅ์ ์ธ์ ๋ ๋ฐ๋น๋กํ๋ ๊ฑฐ๋ผ ์ ์ ํ ์ ์์ ํํ์ ๋ด์ผ ํ๋๋ผ๊ตฌ์.
์ด ๊ธ ์ฝ๊ณ ๋ง์ด ์๊ฐ์ด ์ ๋ฆฌ๋์์ต๋๋ค. ๊ณ ๋ง์ต๋๋ค.
๊น๋ํ ์ ๋ฆฌ์ ์ดํดํ๊ธฐ ์ฌ์ด ์ค๋ช ๋๋ถ์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ฉฐ ๋ง์ ์ฐธ๊ณ ๊ฐ ๋์์ต๋๋ค. ๊ฐ์ฌํฉ๋๋ค!!!
์น์ ํ ํฌ์คํธ ๋๋ถ์ ์ดํด๊ฐ ์ ๋์์ต๋๋ค ;ใ ; ๊ฐ์ฌํฉ๋๋ค!
์๋ ํ์ธ์, ์ฐ์ ์ข์ ํฌ์คํธ ๊ฐ์ฌ๋๋ฆฝ๋๋ค! ์ง๋ฌธ์ด ์๋๋ฐ์, ์ธ์ ID๋ฅผ ๋ธ๋ผ์ฐ์ ์ ์ ์ฅํ๋ ๊ฒ์ด ์ํ์์๊ฐ ์์ด JWT๋ฅผ ์ด์ฉํ๋ค๊ณ ํ์ จ๋๋ฐ, ๊ทธ๋ผ ์ธ์ ID๋ฅผ HTTPOnly๋ก ์ ์ฅํ๋ฉด ๋ฌธ์ ๊ฐ ์๋ ๊ฑด๊ฐ์?
์๋ ํ์ธ์ ๋ฌธ์๋ด์ฉ์ด ์์ด์ ๊ธ์ ๋จ๊น๋๋ค..
๋ก๊ทธ์ธ ์ ์ง์ refresh ํ ํฐ ์ฌ์ฉํด์ ๊ฐฑ์ ์ ํ๋ค๊ณ ํ์
จ๋๋ฐ
refresh ํ ํฐ๋ ์ ํจ๊ธฐ๊ฐ์ด ์๋๋ฐ ๊ทธ ๊ธฐ๊ฐ์กฐ์ฐจ ๋ง๋ฃ๋๋ฉด ์ด๋ป๊ฒ ํด์ผํ๋์?
์ฌ์ฉ์๊ฐ ๋ก๊ทธ์์ ์ด์ ๊น์ง ๋ก๊ทธ์ธ์ ์ง๋ฅผ ํด์ผํ๋ ์ํฉ์ ๋๋ค.
์ ๋ฆฌ๋ฅผ ๋๋ฌด ์ํด์ฃผ์ จ์ด์ ๋ฆฌ์กํธ ์์ ๋ ๋๋ฌด ์ข์ต๋๋ค ๊ฐ์ฌํฉ๋๋ค
์ข์ ๊ธ ์ ๋ดค์ต๋๋ค!
๋ค๋ง ๊ถ๊ธํ ์ ์ด ํ๋ ์์ด์. refresh token์ httpOnly ์ฟ ํค์ ์ ์ฅํ๋ค๊ณ ํ์
จ๋๋ฐ access token์ด ๋ง๋ฃ๋์์ ๋ refresh token์ JS๋ก ์ด๋ป๊ฒ ๊ฐ์ ธ์์ refresh ์์ฒญ์ ํด์ผ ํ ๊น์?
๊ธ ์ ๋ง ๋๋ฌด ์ข์ ๋ด์ฉ์ด์์ ์ด๋ฐ ์ ์ฉํ ๊ธ ์ฌ๋ ค์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค!!! ๋๋ถ์ ๊ณต๋ถ๊ฐ ๋ง์ด ๋ฌ์ด์!!
JSON WEB TOKEN ๊ด๋ จ ๊ธ ์ค์ ๊ฐ์ฅ ์ฝ๊ณ ํ์คํ๊ฒ ์๋ ค์ฃผ๋ ๊ธ์ด ์๋๊ฐ ์ถ์ต๋๋ค.
์ ๋ ์ดํ๋ฆฌ์ผ์ด์
์ธ์ฆ๋ฐฉ๋ฒ์ผ๋ก JWT๋ฅผ ์ฌ์ฉํ๊ณ ์๋๋ฐ ํ ํฐ์ด ํ์ทจ๋นํ๋ฉด
๋ณด์์ ์ทจ์ฝํ๋ค๋๊ฒ์ ์ฒด๊ฐํ๊ณ ๊ฒ์ํ๋ค๊ฐ ์ด๊ณณ์ผ๋ก ์ค๊ฒ ๋์์ด์.
์ ๊ฐ ํ๋ ค๊ณ ํ๋ ๋ฐฉ์์ด ๊ด์ฐฎ์์ง ์กฐ์ธ ์ข ๊ตฌํด ๋ณด์์
๊ทธ๋ฆฌ๊ณ Refresh Token์ ๋ก๊ทธ์์ ํ์ง ์์์ฑ๋ก ์ฌ๋ฐํํ๋ฉด ์๊ธฐ๋ ๋ฌธ์ ๊ฐ ์์๊น์?
๊ธ ์์ฝ์์ต๋๋ค.
ํ๋ ๊ณต์ ๋๋ฆฌ๊ณ ์ถ์๊ฒ
accessToken์ ํ์ทจ๋นํ ๊ฐ๋ฅ์ฑ์ด ์๋ค๋ผ๊ณ ์๊ฐํ๊ณ ๊ฐ๋ฉด accessToken์์ฒด์ ๋ง๋ฃ์๊ฐ์ ๊ทน๋จ์ ์ผ๋ก ์งง๊ฒ(1๋ถ ๋ฏธ๋ง) ์ฃผ์ด์ ํ์ทจ๋ฅผ ๋นํด๋ ์ฌ์ฉํ ์ ์๊ฒ ํ๋๋ฐฉ๋ฒ์ ์ด๋จ์ง ์ฌ์ญค๋ณด๊ณ ์ถ์ต๋๋ค.
์ด๋ ๊ฒ ํ์ ๊ฒฝ์ฐ refreshToken์ secure, httpOnly๋ก ์ธํด ๋น๊ต์ ํ์ทจ ๊ฐ๋ฅ์ฑ์ด ์ ๊ธฐ๋๋ฌธ์ refreshToken์ ์ข ๋ ๋ง์ด ํ์ฉํ๋ ๋ฐฉ์์ผ๋ก ์๊ฐํ์ ๋ accessToken์ ๊ธฐ์กด ์์ฑํ์ ๋ฐฉ๋ฒ๋๋ก localStorage์ ์ ์ฅ์์ผ์ XSS์ ์ํ์ ๋
ธ์ถ๋์ด๋ ํ์ทจ๋ฅผ ๋นํ์๋๋ ์ด๋ฏธ token์ด ๋ง๋ฃ๋์ด ์ฌ์ฉํ ์ ์์ด์ ๋น๊ต์ ์์ ํ๋ค ์๊ฐํฉ๋๋ค.
ํ ํฐ์ ์ถ์ฒํ์ ์ด์ ๊ฐ "์ธ์ฆ ๊ด๋ฌธ์ 2๊ฐ ๋ง๋ค๊ธฐ ์ํจ"์ด๋ผ๊ณ ์์ ์๋ ๋๋๊ธ์์ ์ค๋ช ์ฃผ์ จ๋๋ฐ, ์ธ์ฆ ๊ด๋ฌธ์ด 2๊ฐ์ธ ๊ฒ์ด ์ด๋ค ์ฅ์ ์ด ์๋์? ๋ฆฌํ๋ ์ ํ ํฐ์ ๋ด๊ณ ์๋ ์ฟ ํค๊ฐ CSRF ์ทจ์ฝ์ ์ ๋ ธ์ถ๋๋ค๋ฉด ๊ด๋ฌธ์ด 1๊ฐ์ด๊ฑด, 2๊ฐ์ด๊ฑด ์ํ์ ๋ ธ์ถ๋๋ ๊ฑด ๋๊ฐ์ง ์์๊น์?
onSilentRefresh = () => {
axios.post('/silent-refresh', data)
.then(onLoginSuccess)
.catch(error => {
// ... ๋ก๊ทธ์ธ ์คํจ ์ฒ๋ฆฌ
});
}
์ด๋ถ๋ถ์์ ๋๋ฒ์งธ์ค์ ์๋ data๋ ์ด๋ค ๊ฒ์ ๋งํ๋๊ฑด๊ฐ์?? ์คํ์ธ๊ฐ์??
์ดํฌ์คํธ ๋ณด๊ธฐ์ด์ ์ฐธ๊ณ ์๋ฃ ๋ค๋ณด์์ง๋ง,
์ฐ์ฐํ ๋ค์ด์ค๊ฒ๋๋๋ฐ, ๋๋ฌด ์ ์ ๋ฆฌ๋์ด์์ด ์ดํฌ์คํธ ํ๋๋ง ๋ณด๊ฒ๋๋ค์ ใ
ใ
ใ
ใ
ใ
ใ
ใ
ใ
์ ๋ง์ ์ฉํ๊ฒ ๋ดค์ต๋๋ค ๊ฐ์ฌํฉ๋๋ค
yaytomato๋ ๊ธ ๋๋ถ์ ์ด์ ์์ผ refresh token์ ์ ์ฌ์ฉํ๋์ง ์ดํด๊ฐ ์ ๋๋ก ๋์์ต๋๋ค. ๋ค๋ฅธ ๋ณด์ ์ด์๋ ์ดํด ์ฝ๊ฒ ์ค๋ช ํด์ฃผ์๊ณ ์ข์ ๊ธ ์ ๋ง๋ก ๊ฐ์ฌํฉ๋๋ค~!๐๐๐
๋ณด๋ค๋ณด๋ OAuth ์ค๋ช ์ธ ๊ฒ ๊ฐ์๋ฐ, OAuth ๋ผ๋ ํค์๋๊ฐ ์ ํ ์์ด์ ๋นํฉ ์ค ์ ๋๋ค... JWT์ OAuth ์ ๊ด๊ณ์ ๋ํด์๋ ์งง๊ฒ๋๋ง ์๋ ค์ฃผ์๋ฉด ์ข์ ๊ฒ ๊ฐ์์
์ก์ธ์ค ํ ํฐ์ ์ฐ์ฅํ ๋, onSilentRefresh ํจ์๋ฅผ ์คํ์ํค๊ณ ์๋ก์ด refresh token๊ณผ access token์ ๋ฆฌํด๋ฐ๋๋ค๊ณ ์ ์ผ์
จ๋๋ฐ, refresh token์ ์กด์ฌ ์ด์ ๊ฐ ๊ถ๊ธํฉ๋๋ค.
๊ทธ๋ฅ access token๋ง ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ๊ฐ๋จํ ๊ฒ ์๋๊ฐ์?
์ ์ฉํ ์ ๋ณด ๊ฐ์ฌํฉ๋๋ค.
ํ๋ ๊ถ๊ธํ๊ฒ ์๋๋ฐ accessToken์ ๋ก์ปฌ๋ณ์ ์ฆ ์๋์ ๊ฐ์ด ์ค์ ํ์ ๋ค๊ณ ํ์
จ๋๋ฐ
axios.defaults.headers.common['Authorization'] = Bearer ${accessToken}
;
์ด ๋ํ javascript๋ก ์ ๊ทผ์ด ๊ฐ๋ฅํ์ฌ xss์ ์ทจ์ฝํ ๊ฒ์ด ์๋์ง ๊ถ๊ธํฉ๋๋ค.
๊ฐ์ฌํฉ๋๋ค. ๋๋ถ์ JWT์ ๋ณด์์ ๋ํด ๋ง์ด ๋ฐฐ์๊ฐ๋๋ค!!!!
ํน์ ์ถ์ฒ๋ฅผ ๋จ๊ธฐ๊ณ ์ ๋ธ๋ก๊ทธ์ ๊ธ์ ์ข ๋จ๊ฒจ๋ฌ ๋ ๊น์??
ํน์ ์ฌํ๋ฆฌ์์๋ httponly option ์ผ๋ก refreshToken์ ์ฃผ๊ณ ๋ฐ๋๊ฒ ๊ฐ๋ฅํ๊ฐ์? ๋ง์ฝ ์๋ฒ ๋๋ฉ์ธ๊ณผ ํด๋ผ์ด์ธํธ ๋๋ฉ์ธ์ด ๋ค๋ฅธ ๊ฒฝ์ฐ์๋ ์์ ๊ฐ์ ์ธ์ฆ๋ฐฉ์์ด ๊ฐ๋ฅํ ๊ฒ์ธ์ง ๊ถ๊ธํฉ๋๋ค
์ข์ ๊ธ ๊ฐ์ฌํฉ๋๋ค. ๊ทธ๋ฐ๋ฐ client๋ server๋ก ์์ฒญ์ ๋ณด๋ผ๋ ํญ์ header์ cookie๋ฅผ ๋ด์ ๋ณด๋ด๋ ๊ฑธ๋ก ์๊ณ ์์ต๋๋ค. refresh token๋ฅผ ์ฐ๋ ์ด์ ๋ (access token์ด ๋ง๋ฃ๋์๋ค๋ ์ ํธ๋ฅผ ๋ฐ์๋๋ง ๋ณด๋์ผ๋ก์) ํต์ ํ์ ์์ฒด๋ฅผ ์ค์ฌ ํ์ทจ ๋นํ ์ํ์ ์ค์ด๊ธฐ ์ํจ์ธ๋ฐ, refresh token์ cookie์ ๋ฃ์ผ๋ฉด ๋ชจ๋ ์์ฒญ์ refresh token์ด ํฌํจ๋๋๊น refresh token์ ์ฅ์ ์ด ์ฌ๋ผ์ง๋ ๊ฒ ์๋๊ฐ์?
๊ถ๊ธํด์ ๋ฌผ์ด๋ด ๋๋ค...!
์์ฐ ์ ๋ง ์ข์ ๋ด์ฉ์ด๋ค์!!