์ฟ ํค๋ ์๋ฒ๊ฐ ์ฌ์ฉ์์ ์น ๋ธ๋ผ์ฐ์ ์ ์ ์กํ๋ ์์ ๋ฐ์ดํฐ ์กฐ๊ฐ ์ด๋ค.
secure
์ ๋ฏธ์ฌ๋ฅผ ์ฌ์ฉํ๋ฉด https ์์๋ง ์ฟ ํค๋ฅผ ์ ์กํ๋๋ก ์ค์ ํ ์ ์๋ค.Set-Cookie
header ๋ฅผ ์ ์กํ ์ ์๋ค.๊ฐ๋จํ ์ฟ ํค๋ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ๋ ์ ์๋ค.
Set-Cookie: <cookie-name>=<cookie-value>
์ด ์๋ฒ ํค๋๋ ํด๋ผ์ด์ธํธ์๊ฒ ์ฟ ํค๋ฅผ ์ ์ฅํ๋ผ๊ณ ์ ๋ฌํ๋ค.
# ์๋ฒ ํค๋
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
์ด์ ์๋ฒ๋ก ์ ์ก๋๋ ๋ชจ๋ ์์ฒญ๊ณผ ํจ๊ป, ๋ธ๋ผ์ฐ์ ๋ Cookie header ๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฒ๋ก ์ด์ ์ ์ ์ฅํ๋ ๋ชจ๋ ์ฟ ํค๋ค์ ํ์ ํ๋ค.
# ๋ธ๋ผ์ฐ์
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
์ฟ ํค์ ๋ผ์ดํํ์์ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ์ ์ํ ์ ์๋ค.
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
Expires
์์ฑ์ ๋ช
์๋ ๋ ์ง์ ์ญ์ ๋๊ฑฐ๋,Max-Age
์์ฑ์ ๋ช
์๋ ๊ธฐ๊ฐ ์ดํ์ ์ญ์ ๋๋ค.์ด์ ๋ก๊ทธ์ธ์ ๊ตฌํํ๊ธฐ ์ ์ ๋ณด์์ ๋ํด ์์๋ณด์.
์ ์ฒ๋ผ ์ฟ ํค๋ Client์์ JavaScript ๋ก ์กฐํํ ์ ์๊ธฐ ๋๋ฌธ์ ๋ณด์ ์ค์ ์ ํ์ง ์์ ์ฑ๋ก ํต์ ์ ํด๋ฒ๋ฆฌ๋ฉด ์ฟ ํค๋ฅผ ๊ฐ๋ก์ฑ ์ ์๋ค๋ ๋ฌธ์ ๊ฐ ์๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ํด๋ผ์ด์ธํธ์ ์๋ฒ์์ ์ฌ๋ฌ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ด์ฉํด์ ์ด๋ฐ ๊ณต๊ฒฉ๋ค์ ๋ฐฉ์ดํด์ผ ํ๋ค.
๊ทธ ๋ฐฉ๋ฒ๋ค์ ๋ํด์ ์์๋ณด์.
HttpOnly ์ฟ ํค ์์ฑ์ ์ฌ์ฉํ๋ฉด JavaScript๋ฅผ ํตํด ์ฟ ํค์ ์ ๊ทผํ ์ ์๊ฒ ๋์ด, ์
์ฑ ์คํฌ๋ฆฝํธ๋ฅผ ํตํด ์ฟ ํค ๊ฐ์ ์ ๊ทผํ๋ ๊ฒ์ ๋ง์์ค๋ค.
HTTP Only Cookie ๋ฅผ ํ์ฑํ ํ๊ธฐ ์ํด์๋ ์ฟ ํค๋ฅผ ์์ฑํ ๋, ๊ฐ์ฅ ๋ง์ง๋ง์ HttpOnly
๋ผ๋ ์ ๋ฏธ์ฌ๋ฅผ ์ถ๊ฐํ๋ฉด ๋๋ค.
HTTP Only Cookie๋ฅผ ์ฌ์ฉํ๋ฉด Client์์ Javascript๋ฅผ ํตํ ์ฟ ํค ํ์ทจ๋ฌธ์ ๋ฅผ ์๋ฐฉํ ์ ์๋ค.
ํ์ง๋ง Javascript๊ฐ ์๋ ๋คํธ์ํฌ๋ฅผ ์ง์ ๊ฐ์ฒญํ์ฌ ์ฟ ํค๋ฅผ ๊ฐ๋ก์ฑ ์๋ ์๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ด๋ฌํ ์ ๋ณด ์ ์ถ๋ค์ ๋ง๊ธฐ ์ํด, HTTPS ํ๋กํ ์ฝ์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ํธํํ์ฌ ์๋ฒ์ ๋๊ฒจ์ฃผ๊ฒ ๋๋ฉด, ํด์ปค๋ค์ด ์ฟ ํค๋ฅผ ํ์ทจํด๋ ์ํธํ๊ฐ ๋์ด์์ด ์ ๋ณด๋ฅผ ์์๋ผ ์ ์๋ค.
์ด๋ฅผ ์ํด Secure
์ ๋ฏธ์ฌ๋ฅผ ์ฌ์ฉํด์ ์ฟ ํค๋ฅผ ์์ฑํ๊ฒ ๋๋ฉด ๋ธ๋ผ์ฐ์ ๋ HTTPS ๊ฐ ์๋ ํต์ ์์๋ ์ฟ ํค๋ฅผ ์ ์กํ์ง ์๋๋ค.
HttpOnly ์์ฑ๊ณผ Secure ์์ฑ์ ์ฌ์ฉํด ์์ฑํ ์ฟ ํค์ ์์์ด๋ค.
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
๋ก๊ทธ์ธ ์ธ์ฆ ๋ฐฉ์์๋ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๋ค.
accessToken
๊ณผ refreshToken
์ ํด๋ผ์ด์ธํธ์ ์ ์ฅ โ accessToken
์ ๋ก๊ทธ์ธํ ์ ์ ์๊ฒ๋ง ๋ณด์ฌ์ค ์ ์๋ ์ ๋ณด์ ์ ๊ทผํ ๋ ์๋ฒ์ ์ ์ก โ ํ ํฐ์ด ์ ํจํ์ง ํ์ธaccessToken
์ธ๋ฐ ์ด๋ ์ผ์ ์๊ฐ์ด ์ง๋๋ฉด ๋ง๋ฃ๋๋ค.refreshToken
์ ์ด์ฉํด ๋ก๊ทธ์ธ์ ์ง์์ ์ผ๋ก ์ ์งํ ์ ์๋ค.refreshToken
์ ์๋ฒ์ ์ ์กํ๋ฉด โ ์๋ฒ๋ ์๋ก์ด accessToken
์ ๋ฐ๊ธํด์ค๋ค.์ฐ๋ฆฌ๋ ์ด์ค์์ ํ ํฐ(JWT) ๋ฅผ ์ด์ฉํ ๋ฐฉ์์ผ๋ก ๋ก๊ทธ์ธ์ ๊ตฌํํ๊ธฐ๋ก ํด์ JWT ์ ๋ํด์ ์์๋ดค๋ค.
JSON Web Token ์ด๋ JSON ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ์ ๋ณด๋ฅผ ์์ ํ๊ฒ ์ ๋ฌํ๊ธฐ ์ํ ์ปดํฉํธํ๊ณ ์์ฒด์ ์ธ ๋ฐฉ๋ฒ์ JSON ๊ฐ์ฒด๋ก ์ ์ํ๋ ๊ฐ๋ฐฉํ ํ์ค์ด๋ค.
์ํธํ๋ ์๊ทธ๋์ฒ ์ถ๊ฐ๊ฐ ๊ฐ๋ฅํ ๋ฐ์ดํฐํจํค์ง๋ผ๊ณ ๋ ํ ์ ์๋ค.
JSON Web Token ์ ์ (.)์ผ๋ก ๊ตฌ๋ถ๋ ์ธ ๋ถ๋ถ์ผ๋ก ๊ตฌ์ฑ๋์ด ์๋ค.
- Header
- Payload
- Signature
์ด๊ฒ๋ค์ ํ๋๋ก ํฉ์น๋ฉด ํ๋์ JWT ๊ฐ ๋๋๋ฐ, ์ด๋ ์ ์ผ๋ก ๊ตฌ๋ถ๋ 3๊ฐ์ Base64-URL ๋ฌธ์์ด๋ก, ์ผ๋ฐ์ ์ผ๋ก ๋ค์์ฒ๋ผ ์๊ธฐ๊ฒ ๋๋ค.
xxxxx.yyyyy.zzzzz
์ด๋ ๊ฒ ์์ฑ๋ JWT๋
jwt.io Debugger ๋ฅผ ์ฌ์ฉํด์ ์๋์ฒ๋ผ decode, verify, ๋ฐ ์์ฑ ํ ์ ์๋ค.
์ด์ ์ด JWT ๋ฅผ ์ฌ์ฉํด์ ๋ฆฌ์กํธ์์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ๊ตฌํํด๋ณด์.
https://velog.io/@yaytomato/ํ๋ก ํธ์์-์์ ํ๊ฒ-๋ก๊ทธ์ธ-์ฒ๋ฆฌํ๊ธฐ
์ ๋ธ๋ก๊ทธ๋ฅผ ์ฐธ๊ณ ํด์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ค.
๋ฐฑ์๋์ ๋ง์ถฐ๋ณธ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ์คํ์ ๋ค์๊ณผ ๊ฐ๋ค.
์ด์ ํ๋ก ํธ์์ ํด์ค์ผ ํ๋ ์ผ๋ค์ ์ดํด๋ณด์.
๋ค๋ฅธ ๋๋ฉ์ธ๋ค๋ผ๋ฆฌ ์๊ฒฉ ์ธ์ฆ ์ ๋ณด๋ฅผ ๊ณต์ ํ ์ ์๋๋ก ์ต์ ์ ์ค์ ํ๋ค. (withCredentials = true)
๋ง์ฝ ํด๋ผ์ด์ธํธ ์๋ฒ์ API ์๋ฒ๊ฐ Host ๋ ๊ฐ์ง๋ง, Port ๊ฐ ๋ค๋ฅด๋ค๊ณ ๊ฐ์ ํด๋ณด์.
- ํด๋ผ์ด์ธํธ ์๋ฒ : http://localhost:3000
- API ์๋ฒ : http://localhost:8080
์๋ฌด ์ค์ ๋ ํ์ง ์์ ์ฑ axios ๋ก ๋ก๊ทธ์ธ ์์ฒญ์ ์๋ฒ์ ๋ณด๋ธ๋ค๋ฉด CORS(Cross-origin-resource-sharing ๊ต์ฐจ ์ถ์ฒ ๋ฆฌ์์ค ๊ณต์ ) ์๋ฌ์ ํจ๊ป ๋ธ๋ผ์ฐ์ ์ Cookie ์๋ ์๋ฌด๊ฒ๋ ๋ด๊ฒจ์์ง ์์ ๊ฒ์ด๋ค.
์ฌ๊ธฐ์ ์ฐ์ credential ์ด๋, ์ฟ ํค๋ย Authorization ์ธ์ฆ ํค๋ ๋ฑ์ ๋ดํฌํ๋ ์๊ฒฉ ์ธ์ฆ ์ ๋ณด๋ฅผ ๋งํ๋ค.
๊ทธ๋ฐ๋ฐ ๋ธ๋ผ์ฐ์ ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฟ ํค์ ๊ฐ์ ์ธ์ฆ๊ณผ ๊ด๋ จ๋ ๋ฐ์ดํฐ๋ฅผ ํจ๋ถ๋ก request/response header ์ ๋ด์ง ์๋๋ก ๋์ด์๋ค.
๊ทธ๋์ ๋ง์ฝ ๋ค๋ฅธ ๋๋ฉ์ธ(Cross Origin)๋ผ๋ฆฌ ์ฟ ํค์ ๊ฐ์ ์๊ฒฉ ์ธ์ฆ์ ๋ณด๋ฅผ ๊ณต์ (resource sharing) ํ๊ณ ์ถ๋ค๋ฉด, ํด๋ผ์ด์ธํธ์ ์๋ฒ์์ ์๋์ผ๋ก ์ด๋ฅผ ํ์ฉํ๋ค๋ ์ต์
์ ๊ฐ๊ฐ ์ถ๊ฐํด์ผ ํ๋ค.
ํด๋ผ์ด์ธํธ์์๋ withCredentials ์ต์
์ ์ค์ ํด์ค์ผ ํ๊ณ ,
์๋ฒ์์๋ Access-Control-Allow-Credentials ํค๋๋ฅผ ์ค์ ํด์ฃผ๋ฉด ๋๋ค.
์ฐ๋ฆฌ๋ axios ๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ฏ๋ก, aixos ๊ณต์ docs ๋ฅผ ํตํด withCredentials ์ต์
์ ์ค์ ํด๋ณด์.
withCredentials ์ ์ฃผ์์ ๋ณด๋ฉด ์๊ฒฉ ์ฆ๋ช
(credential)์ ์ฌ์ฉํ์ฌ ์ฌ์ดํธ ๊ฐ ์ ๊ทผ ์ ์ด๋ฅผ ํด์ผ ํ๋์ง ์ฌ๋ถ๋ฅผ ๋ํ๋ธ๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
์ด์ ์ ์ญ Axios Config ์ ์ด withCredentials ์ต์
์ true
๋ก ์ค์ ํด์ฃผ๋ฉด ๋๋ค.
axios.defaults.baseURL = "https://www.abc.com";
axios.defaults.withCredentials = true;
๋ก๊ทธ์ธ API ๋ฅผ ์์ฒญํ๋ค.
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}`;
// local ์ด ์๋ ํ๊ฒฝ์์๋ ํ๋ก ํธ์ ๋ฐฑ์๋๊ฐ ๊ฐ์ ๋๋ฉ์ธ์ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด
// token ์ด Cookie ์ ๋ด๊ฒจ ๋ณด๋ด์ง๊ธฐ ๋๋ฌธ์
// token ์ด ๋ง๋ฃ๋๊ธฐ ์ ๊น์ง๋ ์ถ๊ฐ์ ์ธ ์ค์ ์์ด ๋ค๋ฅธ API๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
// local ์ผ ๊ฒฝ์ฐ์๋ง storage ์ token ์ ์ ์ฅํด์
// API ํต์ ๋ง๋ค ํค๋์ token ์ ๋ด์ ๋ณด๋ด๋๋ก ์ค์ ํ๋ค.
}).catch(error => {
// ... ์๋ฌ ์ฒ๋ฆฌ
// ex) 401 unauthorized ์ผ ๊ฒฝ์ฐ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋ํ๋ค.
});
}
์์ ์ ์ด๋์ ๋ฐ์ ๊ฐ์ด
local ์ด ์๋ ํ๊ฒฝ์์๋ ํ๋ก ํธ์ ๋ฐฑ์๋๊ฐ ๊ฐ์ ๋๋ฉ์ธ์ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด
token ์ด Cookie ์ ๋ด๊ฒจ ๋ณด๋ด์ง๊ธฐ ๋๋ฌธ์ token ์ด ๋ง๋ฃ๋๊ธฐ ์ ๊น์ง๋ ์ถ๊ฐ์ ์ธ ์ค์ ์์ด ๋ค๋ฅธ API๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
๋ง์ฝ token ์ด ๋ง๋ฃ๋ ํ์ ์ฒ๋ฆฌ๋ฅผ ํ๊ณ ์ถ๋ค๋ฉด catch ์ ์์ 401 unauthorized ์ผ ๊ฒฝ์ฐ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋ํ๋๋ก ์ฒ๋ฆฌํ ์ ์์ ๊ฒ์ด๋ค.
๋ํ local ์ผ ๊ฒฝ์ฐ์๋ง ๊ฐ๋ฐ ํธ์์ฑ์ ์ํด storage ์ token ์ ์ ์ฅํด์ API ํต์ ๋ง๋ค ํค๋์ token ์ ๋ด์ ๋ณด๋ด๋๋ก ์ค์ ํ์๋ค.
์ด๋ ๊ฒ ์ฒ๋ฆฌํ๋ฉด ์๋ก๊ณ ์นจ์ด ๋์ด๋ token ์ด ๋ ์๊ฐ๋ ์ผ์ ์์ ๊ฒ์ด๋ค...!
๋ก๊ทธ์ธ ๋ง๋ฃ ๋ฐ ๋ก๊ทธ์ธ ์ฐ์ฅ ์ฒ๋ฆฌ๋ฅผ ํด์ค๋ค.
๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ token ์ด ๋ง๋ฃ๋์์ ๊ฒฝ์ฐ ์ฒ๋ฆฌํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์์ ๊ฒ์ด๋ค.
์ฐ๋ฆฌ๋ API ํต์ ์ 401 unauthorized ์ผ ๊ฒฝ์ฐ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋ํ๋๋ก ์ฒ๋ฆฌํ์๋ค.
์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ ๋ฐฉ๋ฒ ๋์
์ ์ ๊ฐ ๋ชจ๋ฅด๊ฒ ์๋ฒ์์ ์๋ก์ด token ์ ๋ฐ์์์ ์กฐ์ฉํ ์๋์ผ๋ก ๋ก๊ทธ์ธ์ด ์ฐ์ฅ๋๋๋ก ํ๋ ๋ฐฉ๋ฒ์ ๋ง์ ์น์ฌ์ดํธ์์ ์ฌ์ฉํ๊ณค ํ๋๋ฐ,
์ด๋ฅผ Silent Refresh ๋ผ๊ณ ํ๋ค.
์๋ ํ์ธ์! ์ข์ ๊ธ ๊ฐ์ฌํฉ๋๋ค:)
๊ถ๊ธํ๊ฒ ์๋๋ฐ์..
axios.defaults.headers.common['Authorization'] =
Bearer ${accessToken}
;์ ํ ํฐ ์ ์ฅํด๋๋ฉด ์๋ก๊ณ ์นจํด๋ ์๋ ๋ผ๊ฐ๋์?