# 인증과 React에서 구현(feat: Kakao 로그인)

### 소개.

모던 웹서비스에서 Token방식은 많이 사용되고 있다.

여러가지 이유가 소개되고 있지만 내가 Token방식을 선택한 이유는 아래와 같다.

* Stateless 서버(서버에서 상태를 유지하지 않으므로) 서버에 부하를 줄일 수 있다.
* OAuth 도입에 맞춰 하며 확장성이 용이하다

### 서버기반인증(세션방식).

Token과 비교되는 방식으로 Session방식이 있다. 서버에서 상태를 저장하고 사용하는 방식을 취합니다.

주로 문제점으로는 **세션**, **서버 확장성(구조 변경)**, **CORS**가 고려되고 있습니다.

여기서 저는 Stateless에 초점을 맞춰 **세션**, \*\*서버 확장성(구조 변경)\*\*을 해결하고자 했습니다.

<div><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/e1da7990-df45-4204-93e4-96fec566584a/b1c50b0e-f713-4fdc-9231-7c820b4ce689/Untitled.png" alt=""> <figure><img src="https://2477703702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F0zNEVFEcc7Tv0qxrOi2Y%2Fuploads%2FiKBkZQQY88RRrPxr0DTh%2Fimg1.png?alt=media&#x26;token=f3593757-6491-459a-aaee-c93e79db7c20" alt=""><figcaption></figcaption></figure></div>

### 토큰기반인증(Token방식).

기본적으로 로그인 시 토큰을 생성하고 정보를 요청할 때마다 토큰을 검증하는 방식으로 인증이 됩니다.

이를 통해 서버는 로그인 여부를 저장하지 않고 Stateless하게 인증을 제공가능합니다.

* 토큰의 단점으로는 특정 악성유저를 세션과 다르게 막지 못한다는 것인데 해당 부분은 결국 저장소를 추가하는 설계로 해결해야할 것 같습니다.
* 장점으로는 Stateless, 확장성, 웹 표준 준수입니다.

<div><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/e1da7990-df45-4204-93e4-96fec566584a/5d73fda7-e8c9-48a4-94a6-bb8a284ccd43/Untitled.png" alt=""> <figure><img src="https://2477703702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F0zNEVFEcc7Tv0qxrOi2Y%2Fuploads%2FJYKKLKCoRk0z0yTVCjLO%2Fimg2.png?alt=media&#x26;token=bd7e6107-935a-4d42-8ddf-e35f265b3e23" alt=""><figcaption></figcaption></figure></div>

### 어떻게 설계?.

* accessToken(24시간)과 refreshToken(2주) 두 개의 토큰으로 관리
* API 전달 시에는 accessToken는 header의 “Authorization”, refreshToken은 쿠키(httpOnly)
* FE 저장 시에는 accessToken는 sessionStorage, refreshToken은 쿠키(secure httpOnly)

### 어떻게 구현하나?.

#### 로그인은 어떻게?

서버로 로그인 요청을 하면 서버에서 해당 유저 계정 확인 후 정상 동작하면 accessToken JSON payload, refreshToken을 쿠키에 저장해서 응답해준다.

#### **프론트 코드 예시**

```jsx
axios.defaults.baseURL = "<https://www.api.com>";
axios.defaults.withCredentials = true; // cookie를 주고 받기 위함이다
axios.post('/login', { email, password }).then(response => {
	const { accessToken } = response.data;

	axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;

	setStorage("accessToken", accessToken); // sessionStorage에 accessToken 저장

}).catch(error => {
});
```

#### 갱신은 어떻게?

accessToken 만료되었을 때(API 호출 중, 앱 다시 실행시켰을 때) 서버로 갱신 요청을 하면 refreshToken이 만료되지 않았다면 새로운 accessToken과 refreshToken으로 갱신시켜줍니다.

**프론트 코드 예시**

```jsx
axios.post('/refresh', data)
.then((response) => {
		const { accessToken } = response.data;

    axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;

		setStorage("accessToken", accessToken); // sessionStorage에 accessToken 저장
})
.catch(error => {
});
```

```jsx
const App = () => {
	...

	useEffect(() => {
		onRefresh(); // 앱 마운트 시 갱신
	});

	...
}

ReactDOM.createRoot(
  document.getElementById('root')
).render(<App />);
```

#### 로그아웃은 어떻게?

refreshToken과 accessToken을 삭제해주면 됩니다.(서버에서의 처리에 따라 서버에 요청)

#### OAuth(여기서는 Kakao)에서 고려할 점?

인가코드를 받아 토큰을 발급받은 후 아래의 로직이 끝난 후 해당 토큰을 우리 서비스의 accessToken과 refreshToken으로 변환해주는 작업이 추가적으로 필요하다.

<div><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/e1da7990-df45-4204-93e4-96fec566584a/2c4e984f-d4a2-42c0-8169-3e77cef288fb/Untitled.png" alt=""> <figure><img src="https://2477703702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F0zNEVFEcc7Tv0qxrOi2Y%2Fuploads%2FmAGeeEdv5Rwoocoo5CwW%2Fimg3.png?alt=media&#x26;token=5423ec8b-8b93-428c-a17f-4dfd2447cf21" alt=""><figcaption></figcaption></figure></div>

### Q\&A.

* 왜 쿠키에 저장?
  * 상대적으로 XSS과 CSRF 취약점 공격에 안전할 수 있기 때문입니다.
* XSS란?
  * 스크립트 주입 공격(React는 jsx로 렌더링하기 전에 이스케이프된다)
* CSRF란?
  * 사이트간 위조 공격(Cookie의 경우 origin에서만 동작하므로 막을 수 있다)
* 쿠키의 secure, httpOnly 옵션이란?
  * secure는 https에서만 사용하도록 하는 옵션
  * httpOnly의 경우 javascript로 document.cookie를 조회하지 못하도록 막는 옵션

### References

[\[JWT\] 토큰(Token) 기반 인증에 대한 소개](https://velopert.com/2350)

[🍪 프론트에서 안전하게 로그인 처리하기 (ft. React)](https://velog.io/@yaytomato/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%90%EC%84%9C-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0#%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%A7%8C%EB%A3%8C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%97%B0%EC%9E%A5-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0)
